2 Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
3 miniaudio - v0.10.33 - 2021-04-04
5 David Reid - mackron@gmail.com
7 Website: https://miniaud.io
8 Documentation: https://miniaud.io/docs
9 GitHub: https://github.com/mackron/miniaudio
15 miniaudio is a single file library for audio playback and capture. To use it, do the following in one .c file:
18 #define MINIAUDIO_IMPLEMENTATION
19 #include "miniaudio.h"
22 You can do `#include "miniaudio.h"` in other parts of the program just like any other header.
24 miniaudio 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,
25 and 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
26 initializing the device.
28 When 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
29 the callback, the size of the internal buffer and the ID of the device you want to emit or capture audio from.
31 Once 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
32 beforehand. This gives the application complete control over how the memory is allocated. In the example below we initialize a playback device on the stack,
33 but you could allocate it on the heap if that suits your situation better.
36 void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
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
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).
53 if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) {
54 return -1; // Failed to initialize the device.
57 ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually.
59 // Do something here. Probably your program's main loop.
61 ma_device_uninit(&device); // This will stop the device so no need to do that manually.
66 In 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
67 from 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
68 extract 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
69 buffer. 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.
70 The 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
71 device 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
72 a 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
73 for the second frame, etc.
75 The 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
76 important 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
77 are 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()`
78 takes 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
79 backends). The `config.playback.format` member sets the sample format which can be one of the following (all formats are native-endian):
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 +---------------+----------------------------------------+---------------------------+
91 The `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
93 44100 or 48000, but can be set to anything. It's recommended to keep this between 8000 and 384000, however.
95 Note 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
96 which is useful if you want to avoid the overhead of miniaudio's automatic data conversion.
98 In 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
99 not 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
100 structures are transparent.
102 Initializing 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
104 it, 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.
105 Note 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
106 event indicating that the device needs to stop and handle it in a different thread. The following APIs must never be called inside the callback:
116 You 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
117 are 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
118 real-time processing thing which is beyond the scope of this introduction.
120 The 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
121 from `ma_device_type_playback` to `ma_device_type_capture` when setting up the config, like so:
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;
129 In 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
130 device type is set to `ma_device_type_capture`).
132 These are the available device types and how you should handle the buffers in the callback:
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 +-------------------------+--------------------------------------------------------+
143 You 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
144 data 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
145 channel), 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
146 will need to convert the data yourself. There are functions available to help you do this which will be explained later.
148 The 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
149 devices connected and you want to use a specific one you will need to specify the device ID in the configuration, like so:
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.
156 To retrieve the device ID you will need to perform device enumeration, however this requires the use of a new concept called the "context". Conceptually
157 speaking 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
158 and to perform operations outside the scope of an individual device. Mainly it is used for performing run-time linking against backend libraries, initializing
159 backends and enumerating devices. The example below shows how to enumerate devices.
163 if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) {
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) {
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);
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;
190 if (ma_device_init(&context, &config, &device) != MA_SUCCESS) {
196 ma_device_uninit(&device);
197 ma_context_uninit(&context);
200 The 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`
201 values 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
202 parameter 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
203 which can be NULL, in which case defaults are used. The context configuration is used for setting the logging callback, custom memory allocation callbacks,
204 user-defined data and some backend-specific configurations.
206 Once 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
207 callback for handling devices by using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer to a pointer that will,
208 upon 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
209 receive the number of items in the returned buffer. Do not free the returned buffers as their memory is managed internally by miniaudio.
211 The `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
212 for presenting a list of devices to the user via the UI.
214 When 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,
215 will 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
216 only 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
217 allocate memory for the context.
223 miniaudio should work cleanly out of the box without the need to download or install any dependencies. See below for platform-specific details.
228 The Windows build should compile cleanly on all popular compilers without the need to configure any include paths nor link to any libraries.
232 The 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
233 compiled 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
234 requires linking to `-lpthread` and `-lm`.
236 Due 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
237 first is to use the `MA_NO_RUNTIME_LINKING` option, like so:
241 #define MA_NO_RUNTIME_LINKING
243 #define MINIAUDIO_IMPLEMENTATION
244 #include "miniaudio.h"
247 This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioUnit`. Alternatively, if you would rather keep using runtime
248 linking you can add the following to your entitlements.xcent file:
251 <key>com.apple.security.cs.allow-dyld-environment-variables</key>
253 <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
260 The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any development packages.
264 The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses sndio and FreeBSD uses OSS.
268 AAudio is the highest priority backend on Android. This should work out of the box without needing any kind of compiler configuration. Support for AAudio
269 starts with Android 8 which means older versions will fall back to OpenSL|ES which requires API level 16+.
271 There 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
272 this happens on your platform you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES.
276 The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. You cannot use -std=c* compiler flags, nor -ansi.
281 `#define` these options before including miniaudio.h.
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 |
380 | | MA_NO_DEVICE_IO |
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. |
399 | | You may need to enable this if your target platform does not allow |
400 | | runtime linking via `dlopen()`. |
401 +----------------------------------+--------------------------------------------------------------------+
402 | MA_LOG_LEVEL [level] | Sets the logging level. Set `level` to one of the following: |
405 | | MA_LOG_LEVEL_VERBOSE |
406 | | MA_LOG_LEVEL_INFO |
407 | | MA_LOG_LEVEL_WARNING |
408 | | MA_LOG_LEVEL_ERROR |
410 +----------------------------------+--------------------------------------------------------------------+
411 | MA_DEBUG_OUTPUT | Enable `printf()` debug output. |
412 +----------------------------------+--------------------------------------------------------------------+
413 | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to |
414 | | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. |
415 +----------------------------------+--------------------------------------------------------------------+
416 | MA_API | Controls how public APIs should be decorated. Default is `extern`. |
417 +----------------------------------+--------------------------------------------------------------------+
418 | MA_DLL | If set, configures MA_API to either import or export APIs |
419 | | depending on whether or not the implementation is being defined. |
420 | | If defining the implementation, MA_API will be configured to |
421 | | export. Otherwise it will be configured to import. This has no |
422 | | effect if MA_API is defined externally. |
423 +----------------------------------+--------------------------------------------------------------------+
428 This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity in the use of terms throughout the audio space, so this
429 section is intended to clarify how miniaudio uses each term.
433 A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit floating point number.
435 3.2. Frame / PCM Frame
436 ----------------------
437 A 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
438 is 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
439 needs 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".
443 A 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
444 stereo 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
445 a 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
446 should not be confused.
450 The 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.
454 Throughout miniaudio you will see references to different sample formats:
456 +---------------+----------------------------------------+---------------------------+
457 | Symbol | Description | Range |
458 +---------------+----------------------------------------+---------------------------+
459 | ma_format_f32 | 32-bit floating point | [-1, 1] |
460 | ma_format_s16 | 16-bit signed integer | [-32768, 32767] |
461 | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] |
462 | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] |
463 | ma_format_u8 | 8-bit unsigned integer | [0, 255] |
464 +---------------+----------------------------------------+---------------------------+
466 All formats are native-endian.
472 The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from devices and can be used independently. The following formats are
475 +---------+------------------+----------+
476 | Format | Decoding Backend | Built-In |
477 +---------+------------------+----------+
478 | WAV | dr_wav | Yes |
479 | MP3 | dr_mp3 | Yes |
480 | FLAC | dr_flac | Yes |
481 | Vorbis | stb_vorbis | No |
482 +---------+------------------+----------+
484 Vorbis is supported via stb_vorbis which can be enabled by including the header section before the implementation of miniaudio, like the following:
487 #define STB_VORBIS_HEADER_ONLY
488 #include "extras/stb_vorbis.c" // Enables Vorbis decoding.
490 #define MINIAUDIO_IMPLEMENTATION
491 #include "miniaudio.h"
493 // The stb_vorbis implementation must come after the implementation of miniaudio.
494 #undef STB_VORBIS_HEADER_ONLY
495 #include "extras/stb_vorbis.c"
498 A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (https://github.com/mackron/miniaudio).
500 Built-in decoders are amalgamated into the implementation section of miniaudio. You can disable the built-in decoders by specifying one or more of the
501 following options before the miniaudio implementation:
509 Disabling built-in decoding libraries is useful if you use these libraries independantly of the `ma_decoder` API.
511 A 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
512 with `ma_decoder_init()`. Here is an example for loading a decoder from a file:
516 ma_result result = ma_decoder_init_file("MySong.mp3", NULL, &decoder);
517 if (result != MA_SUCCESS) {
518 return false; // An error occurred.
523 ma_decoder_uninit(&decoder);
526 When 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
527 to configure the output format, channel count, sample rate and channel map:
530 ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000);
533 When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the same as that defined by the decoding backend.
535 Data 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
536 PCM frames it means you've reached the end:
539 ma_uint64 framesRead = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead);
540 if (framesRead < framesToRead) {
545 You can also seek to a specific frame like so:
548 ma_result result = ma_decoder_seek_to_pcm_frame(pDecoder, targetFrame);
549 if (result != MA_SUCCESS) {
550 return false; // An error occurred.
554 If you want to loop back to the start, you can simply seek back to the first PCM frame:
557 ma_decoder_seek_to_pcm_frame(pDecoder, 0);
560 When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding backend. This can be unnecessarily inefficient if the type
561 is already known. In this case you can use the `_wav`, `_mp3`, etc. varients of the aforementioned initialization APIs:
564 ma_decoder_init_wav()
565 ma_decoder_init_mp3()
566 ma_decoder_init_memory_wav()
567 ma_decoder_init_memory_mp3()
568 ma_decoder_init_file_wav()
569 ma_decoder_init_file_mp3()
573 The `ma_decoder_init_file()` API will try using the file extension to determine which decoding backend to prefer.
579 The `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
580 implementation section of miniaudio. This can be disabled by specifying the following option before the implementation of miniaudio:
586 An 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
587 example for initializing an encoder to output to a file.
590 ma_encoder_config config = ma_encoder_config_init(ma_resource_format_wav, FORMAT, CHANNELS, SAMPLE_RATE);
592 ma_result result = ma_encoder_init_file("my_file.wav", &config, &encoder);
593 if (result != MA_SUCCESS) {
599 ma_encoder_uninit(&encoder);
602 When 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
603 sample format, output channel count and output sample rate. The following file types are supported:
605 +------------------------+-------------+
606 | Enum | Description |
607 +------------------------+-------------+
608 | ma_resource_format_wav | WAV |
609 +------------------------+-------------+
611 If 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
612 you 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:
615 framesWritten = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite);
618 Encoders must be uninitialized with `ma_encoder_uninit()`.
623 A data conversion API is included with miniaudio which supports the majority of data conversion requirements. This supports conversion between sample formats,
624 channel counts (with channel mapping) and sample rates.
627 6.1. Sample Format Conversion
628 -----------------------------
629 Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and `ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()`
630 to 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
631 PCM frames where you want to specify the frame count and channel count as a variable instead of the total sample count.
636 Dithering can be set using the ditherMode parameter.
638 The different dithering modes include the following, in order of efficiency:
640 +-----------+--------------------------+
641 | Type | Enum Token |
642 +-----------+--------------------------+
643 | None | ma_dither_mode_none |
644 | Rectangle | ma_dither_mode_rectangle |
645 | Triangle | ma_dither_mode_triangle |
646 +-----------+--------------------------+
648 Note 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.
649 Dithering is available for the following conversions:
661 Note 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.
665 6.2. Channel Conversion
666 -----------------------
667 Channel conversion is used for channel rearrangement and conversion from one channel count to another. The `ma_channel_converter` API is used for channel
668 conversion. Below is an example of initializing a simple channel converter which converts from mono to stereo.
671 ma_channel_converter_config config = ma_channel_converter_config_init(
672 ma_format, // Sample format
674 NULL, // Input channel map
675 2, // Output channels
676 NULL, // Output channel map
677 ma_channel_mix_mode_default); // The mixing algorithm to use when combining channels.
679 result = ma_channel_converter_init(&config, &converter);
680 if (result != MA_SUCCESS) {
685 To perform the conversion simply call `ma_channel_converter_process_pcm_frames()` like so:
688 ma_result result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount);
689 if (result != MA_SUCCESS) {
694 It is up to the caller to ensure the output buffer is large enough to accomodate the new PCM frames.
696 Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported.
699 6.2.1. Channel Mapping
700 ----------------------
701 In addition to converting from one channel count to another, like the example above, the channel converter can also be used to rearrange channels. When
702 initializing 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
703 channel 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,
704 however, 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
705 specified when initializing the `ma_channel_converter_config` object.
707 When 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
708 channel is simply averaged and copied to the mono channel.
710 In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess channels and silence extra channels. For example, converting
711 from 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.
713 The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a simple distribution between input and output. Imagine sitting
714 in 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
715 of the front and left walls.
717 Finally, 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
718 `ma_channel_converter_config_init()`.
720 Predefined 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
721 be one of the following:
723 +-----------------------------------+-----------------------------------------------------------+
724 | Name | Description |
725 +-----------------------------------+-----------------------------------------------------------+
726 | ma_standard_channel_map_default | Default channel map used by miniaudio. See below. |
727 | ma_standard_channel_map_microsoft | Channel map used by Microsoft's bitfield channel maps. |
728 | ma_standard_channel_map_alsa | Default ALSA channel map. |
729 | ma_standard_channel_map_rfc3551 | RFC 3551. Based on AIFF. |
730 | ma_standard_channel_map_flac | FLAC channel map. |
731 | ma_standard_channel_map_vorbis | Vorbis channel map. |
732 | ma_standard_channel_map_sound4 | FreeBSD's sound(4). |
733 | ma_standard_channel_map_sndio | sndio channel map. http://www.sndio.org/tips.html. |
734 | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering |
735 +-----------------------------------+-----------------------------------------------------------+
737 Below are the channel maps used by default in miniaudio (`ma_standard_channel_map_default`):
739 +---------------+---------------------------------+
740 | Channel Count | Mapping |
741 +---------------+---------------------------------+
742 | 1 (Mono) | 0: MA_CHANNEL_MONO |
743 +---------------+---------------------------------+
744 | 2 (Stereo) | 0: MA_CHANNEL_FRONT_LEFT <br> |
745 | | 1: MA_CHANNEL_FRONT_RIGHT |
746 +---------------+---------------------------------+
747 | 3 | 0: MA_CHANNEL_FRONT_LEFT <br> |
748 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
749 | | 2: MA_CHANNEL_FRONT_CENTER |
750 +---------------+---------------------------------+
751 | 4 (Surround) | 0: MA_CHANNEL_FRONT_LEFT <br> |
752 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
753 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
754 | | 3: MA_CHANNEL_BACK_CENTER |
755 +---------------+---------------------------------+
756 | 5 | 0: MA_CHANNEL_FRONT_LEFT <br> |
757 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
758 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
759 | | 3: MA_CHANNEL_BACK_LEFT <br> |
760 | | 4: MA_CHANNEL_BACK_RIGHT |
761 +---------------+---------------------------------+
762 | 6 (5.1) | 0: MA_CHANNEL_FRONT_LEFT <br> |
763 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
764 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
765 | | 3: MA_CHANNEL_LFE <br> |
766 | | 4: MA_CHANNEL_SIDE_LEFT <br> |
767 | | 5: MA_CHANNEL_SIDE_RIGHT |
768 +---------------+---------------------------------+
769 | 7 | 0: MA_CHANNEL_FRONT_LEFT <br> |
770 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
771 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
772 | | 3: MA_CHANNEL_LFE <br> |
773 | | 4: MA_CHANNEL_BACK_CENTER <br> |
774 | | 4: MA_CHANNEL_SIDE_LEFT <br> |
775 | | 5: MA_CHANNEL_SIDE_RIGHT |
776 +---------------+---------------------------------+
777 | 8 (7.1) | 0: MA_CHANNEL_FRONT_LEFT <br> |
778 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
779 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
780 | | 3: MA_CHANNEL_LFE <br> |
781 | | 4: MA_CHANNEL_BACK_LEFT <br> |
782 | | 5: MA_CHANNEL_BACK_RIGHT <br> |
783 | | 6: MA_CHANNEL_SIDE_LEFT <br> |
784 | | 7: MA_CHANNEL_SIDE_RIGHT |
785 +---------------+---------------------------------+
786 | Other | All channels set to 0. This |
787 | | is equivalent to the same |
788 | | mapping as the device. |
789 +---------------+---------------------------------+
795 Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something like the following:
798 ma_resampler_config config = ma_resampler_config_init(
803 ma_resample_algorithm_linear);
805 ma_resampler resampler;
806 ma_result result = ma_resampler_init(&config, &resampler);
807 if (result != MA_SUCCESS) {
808 // An error occurred...
812 Do the following to uninitialize the resampler:
815 ma_resampler_uninit(&resampler);
818 The following example shows how data can be processed
821 ma_uint64 frameCountIn = 1000;
822 ma_uint64 frameCountOut = 2000;
823 ma_result result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);
824 if (result != MA_SUCCESS) {
825 // An error occurred...
828 // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the
829 // number of output frames written.
832 To 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
833 you want to use, the number of channels, the input and output sample rate, and the algorithm.
835 The 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
836 where necessary. Note that the format is the same for both input and output. The format cannot be changed after initialization.
838 The resampler supports multiple channels and is always interleaved (both input and output). The channel count cannot be changed after initialization.
840 The 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
841 only configuration property that can be changed after initialization.
843 The miniaudio resampler supports multiple algorithms:
845 +-----------+------------------------------+
846 | Algorithm | Enum Token |
847 +-----------+------------------------------+
848 | Linear | ma_resample_algorithm_linear |
849 | Speex | ma_resample_algorithm_speex |
850 +-----------+------------------------------+
852 Because 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
853 it'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
854 the Speex Resampler section below.
856 The algorithm cannot be changed after initialization.
858 Processing always happens on a per PCM frame basis and always assumes interleaved input and output. De-interleaved processing is not supported. To process
859 frames, 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
860 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
861 number 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
862 buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated as seek.
864 The 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
865 ratio with `ma_resampler_set_rate_ratio()`. The ratio is in/out.
867 Sometimes 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
868 `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
869 input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`.
871 Due 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
872 with `ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`.
875 6.3.1. Resampling Algorithms
876 ----------------------------
877 The choice of resampling algorithm depends on your situation and requirements. The linear resampler is the most efficient and has the least amount of latency,
878 but at the expense of poorer quality. The Speex resampler is higher quality, but slower with more latency. It also performs several heap allocations internally
879 for memory management.
882 6.3.1.1. Linear Resampling
883 --------------------------
884 The 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
885 may make it a suitable option depending on your requirements.
887 The linear resampler performs low-pass filtering before or after downsampling or upsampling, depending on the sample rates you're converting between. When
888 decreasing the sample rate, the low-pass filter will be applied before downsampling. When increasing the rate it will be performed after upsampling. By default
889 a fourth order low-pass filter will be applied. This can be configured via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering.
891 The 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
892 can 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
893 sense 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.
894 Values 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
895 and is a purely perceptual configuration.
897 The API for the linear resampler is the same as the main resampler API, only it's called `ma_linear_resampler`.
900 6.3.1.2. Speex Resampling
901 -------------------------
902 The 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
903 domain, 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
904 source files. To opt-in, you must first `#include` the following file before the implementation of miniaudio.h:
907 #include "extras/speex_resampler/ma_speex_resampler.h"
910 Both the header and implementation is contained within the same file. The implementation can be included in your program like so:
913 #define MINIAUDIO_SPEEX_RESAMPLER_IMPLEMENTATION
914 #include "extras/speex_resampler/ma_speex_resampler.h"
917 Note 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
918 initializing. If you try to use the Speex resampler without opting in, initialization of the `ma_resampler` object will fail with `MA_NO_BACKEND`.
920 The 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
921 the fastest with the poorest quality and 10 being the slowest with the highest quality. The default value is 3.
925 6.4. General Data Conversion
926 ----------------------------
927 The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and resampling into one operation. This is what miniaudio uses
928 internally 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
929 conversion is very similar to the resampling API. Create a `ma_data_converter` object like this:
932 ma_data_converter_config config = ma_data_converter_config_init(
941 ma_data_converter converter;
942 ma_result result = ma_data_converter_init(&config, &converter);
943 if (result != MA_SUCCESS) {
944 // An error occurred...
948 In 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
949 channel maps and resampling quality. Something like the following may be more suitable depending on your requirements:
952 ma_data_converter_config config = ma_data_converter_config_init_default();
953 config.formatIn = inputFormat;
954 config.formatOut = outputFormat;
955 config.channelsIn = inputChannels;
956 config.channelsOut = outputChannels;
957 config.sampleRateIn = inputSampleRate;
958 config.sampleRateOut = outputSampleRate;
959 ma_get_standard_channel_map(ma_standard_channel_map_flac, config.channelCountIn, config.channelMapIn);
960 config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER;
963 Do the following to uninitialize the data converter:
966 ma_data_converter_uninit(&converter);
969 The following example shows how data can be processed
972 ma_uint64 frameCountIn = 1000;
973 ma_uint64 frameCountOut = 2000;
974 ma_result result = ma_data_converter_process_pcm_frames(&converter, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);
975 if (result != MA_SUCCESS) {
976 // An error occurred...
979 // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number
980 // of output frames written.
983 The data converter supports multiple channels and is always interleaved (both input and output). The channel count cannot be changed after initialization.
985 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 only
986 configuration property that can be changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of `ma_data_converter_config` is
987 set 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
988 resampling algorithm cannot be changed after initialization.
990 Processing always happens on a per PCM frame basis and always assumes interleaved input and output. De-interleaved processing is not supported. To process
991 frames, 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
992 of 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
993 number 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
994 buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated as seek.
996 Sometimes 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
997 `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
998 input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`.
1000 Due 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
1001 input rate and the output rate with `ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`.
1008 7.1. Biquad Filtering
1009 ---------------------
1010 Biquad filtering is achieved with the `ma_biquad` API. Example:
1013 ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);
1014 ma_result result = ma_biquad_init(&config, &biquad);
1015 if (result != MA_SUCCESS) {
1021 ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount);
1024 Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0, b1 and b2, and the denominator coefficients are a0, a1 and
1025 a2. The a0 coefficient is required and coefficients must not be pre-normalized.
1027 Supported 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
1028 `ma_format_s16` the biquad filter will use fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used.
1030 Input and output frames are always interleaved.
1032 Filtering can be applied in-place by passing in the same pointer for both the input and output buffers, like so:
1035 ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount);
1038 If 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
1039 need 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
1040 do a full initialization which involves clearing the registers to 0. Note that changing the format or channel count after initialization is invalid and will
1044 7.2. Low-Pass Filtering
1045 -----------------------
1046 Low-pass filtering is achieved with the following APIs:
1048 +---------+------------------------------------------+
1049 | API | Description |
1050 +---------+------------------------------------------+
1051 | ma_lpf1 | First order low-pass filter |
1052 | ma_lpf2 | Second order low-pass filter |
1053 | ma_lpf | High order low-pass filter (Butterworth) |
1054 +---------+------------------------------------------+
1056 Low-pass filter example:
1059 ma_lpf_config config = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
1060 ma_result result = ma_lpf_init(&config, &lpf);
1061 if (result != MA_SUCCESS) {
1067 ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount);
1070 Supported 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
1071 frames are always interleaved.
1073 Filtering can be applied in-place by passing in the same pointer for both the input and output buffers, like so:
1076 ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount);
1079 The 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.
1082 for (iFilter = 0; iFilter < filterCount; iFilter += 1) {
1083 ma_lpf2_process_pcm_frames(&lpf2[iFilter], pMyData, pMyData, frameCount);
1087 If 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
1088 useful if you need to change the sample rate and/or cutoff frequency dynamically while maintaing smooth transitions. Note that changing the format or channel
1089 count after initialization is invalid and will result in an error.
1091 The `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
1092 need a second order filter you can use `ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient.
1094 If 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
1095 will be applied, followed by a series of second order filters in a chain.
1098 7.3. High-Pass Filtering
1099 ------------------------
1100 High-pass filtering is achieved with the following APIs:
1102 +---------+-------------------------------------------+
1103 | API | Description |
1104 +---------+-------------------------------------------+
1105 | ma_hpf1 | First order high-pass filter |
1106 | ma_hpf2 | Second order high-pass filter |
1107 | ma_hpf | High order high-pass filter (Butterworth) |
1108 +---------+-------------------------------------------+
1110 High-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
1114 7.4. Band-Pass Filtering
1115 ------------------------
1116 Band-pass filtering is achieved with the following APIs:
1118 +---------+-------------------------------+
1119 | API | Description |
1120 +---------+-------------------------------+
1121 | ma_bpf2 | Second order band-pass filter |
1122 | ma_bpf | High order band-pass filter |
1123 +---------+-------------------------------+
1125 Band-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
1126 usage. 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
1130 7.5. Notch Filtering
1131 --------------------
1132 Notch filtering is achieved with the following APIs:
1134 +-----------+------------------------------------------+
1135 | API | Description |
1136 +-----------+------------------------------------------+
1137 | ma_notch2 | Second order notching filter |
1138 +-----------+------------------------------------------+
1141 7.6. Peaking EQ Filtering
1142 -------------------------
1143 Peaking filtering is achieved with the following APIs:
1145 +----------+------------------------------------------+
1146 | API | Description |
1147 +----------+------------------------------------------+
1148 | ma_peak2 | Second order peaking filter |
1149 +----------+------------------------------------------+
1152 7.7. Low Shelf Filtering
1153 ------------------------
1154 Low shelf filtering is achieved with the following APIs:
1156 +-------------+------------------------------------------+
1157 | API | Description |
1158 +-------------+------------------------------------------+
1159 | ma_loshelf2 | Second order low shelf filter |
1160 +-------------+------------------------------------------+
1162 Where 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.
1165 7.8. High Shelf Filtering
1166 -------------------------
1167 High shelf filtering is achieved with the following APIs:
1169 +-------------+------------------------------------------+
1170 | API | Description |
1171 +-------------+------------------------------------------+
1172 | ma_hishelf2 | Second order high shelf filter |
1173 +-------------+------------------------------------------+
1175 The 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
1176 adjust the volume of low frequencies, the high shelf filter does the same thing for high frequencies.
1181 8. Waveform and Noise Generation
1182 ================================
1186 miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved with the `ma_waveform` API. Example:
1189 ma_waveform_config config = ma_waveform_config_init(
1193 ma_waveform_type_sine,
1197 ma_waveform waveform;
1198 ma_result result = ma_waveform_init(&config, &waveform);
1199 if (result != MA_SUCCESS) {
1205 ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount);
1208 The amplitude, frequency, type, and sample rate can be changed dynamically with `ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`,
1209 `ma_waveform_set_type()`, and `ma_waveform_set_sample_rate()` respectively.
1211 You 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
1214 Below are the supported waveform types:
1216 +---------------------------+
1218 +---------------------------+
1219 | ma_waveform_type_sine |
1220 | ma_waveform_type_square |
1221 | ma_waveform_type_triangle |
1222 | ma_waveform_type_sawtooth |
1223 +---------------------------+
1229 miniaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example:
1232 ma_noise_config config = ma_noise_config_init(
1235 ma_noise_type_white,
1240 ma_result result = ma_noise_init(&config, &noise);
1241 if (result != MA_SUCCESS) {
1247 ma_noise_read_pcm_frames(&noise, pOutput, frameCount);
1250 The noise API uses simple LCG random number generation. It supports a custom seed which is useful for things like automated testing requiring reproducibility.
1251 Setting the seed to zero will default to `MA_DEFAULT_LCG_SEED`.
1253 The amplitude, seed, and type can be changed dynamically with `ma_noise_set_amplitude()`, `ma_noise_set_seed()`, and `ma_noise_set_type()` respectively.
1255 By 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
1256 side. To instead have each channel use the same random value, set the `duplicateChannels` member of the noise config to true, like so:
1259 config.duplicateChannels = MA_TRUE;
1262 Below are the supported noise types.
1264 +------------------------+
1266 +------------------------+
1267 | ma_noise_type_white |
1268 | ma_noise_type_pink |
1269 | ma_noise_type_brownian |
1270 +------------------------+
1276 miniaudio 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
1277 can also handle the memory management for you internally. Memory management is flexible and should support most use cases.
1279 Audio buffers are initialised using the standard configuration system used everywhere in miniaudio:
1282 ma_audio_buffer_config config = ma_audio_buffer_config_init(
1287 &allocationCallbacks);
1289 ma_audio_buffer buffer;
1290 result = ma_audio_buffer_init(&config, &buffer);
1291 if (result != MA_SUCCESS) {
1297 ma_audio_buffer_uninit(&buffer);
1300 In 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
1301 would rather make a copy of the data, use `ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`.
1303 Sometimes 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,
1304 the raw audio data will be located immediately after the `ma_audio_buffer` structure. To do this, use `ma_audio_buffer_alloc_and_init()`:
1307 ma_audio_buffer_config config = ma_audio_buffer_config_init(
1312 &allocationCallbacks);
1314 ma_audio_buffer* pBuffer
1315 result = ma_audio_buffer_alloc_and_init(&config, &pBuffer);
1316 if (result != MA_SUCCESS) {
1322 ma_audio_buffer_uninit_and_free(&buffer);
1325 If 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,
1326 the memory pointed to by `pExistingData` will be copied into the buffer, which is contrary to the behavior of `ma_audio_buffer_init()`.
1328 An 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
1329 used 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
1330 means 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
1331 with with `ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an audio buffer.
1334 ma_uint64 framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping);
1335 if (framesRead < desiredFrameCount) {
1336 // If not looping, this means the end has been reached. This should never happen in looping mode with valid input.
1340 Sometimes 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
1341 pointer to a segment of data:
1344 void* pMappedFrames;
1345 ma_uint64 frameCount = frameCountToTryMapping;
1346 ma_result result = ma_audio_buffer_map(pAudioBuffer, &pMappedFrames, &frameCount);
1347 if (result == MA_SUCCESS) {
1348 // Map was successful. The value in frameCount will be how many frames were _actually_ mapped, which may be
1349 // less due to the end of the buffer being reached.
1350 ma_copy_pcm_frames(pFramesOut, pMappedFrames, frameCount, pAudioBuffer->format, pAudioBuffer->channels);
1352 // You must unmap the buffer.
1353 ma_audio_buffer_unmap(pAudioBuffer, frameCount);
1357 When 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
1358 you 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
1359 for 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
1360 `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()`.
1366 miniaudio 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
1367 on 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`.
1369 Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved streams. The caller can also allocate their own backing memory for
1370 the ring buffer to use internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for you.
1372 The 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
1373 something like the following:
1377 ma_result result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb);
1378 if (result != MA_SUCCESS) {
1383 The `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
1384 ring 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
1385 fourth parameter is an optional pre-allocated buffer and the fifth parameter is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation
1386 routines. Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used.
1388 Use `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
1389 sub-buffers you can use `ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and `ma_pcm_rb_get_subbuffer_ptr()`.
1391 Use `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
1392 need, 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
1393 a 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.
1395 After 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
1396 `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
1397 call 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
1398 `ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was originally requested.
1400 If 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()`,
1401 `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
1402 the 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
1403 there is too little space between the pointers, move the write pointer forward.
1405 You 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`
1406 functions instead of `ma_pcm_rb` and instead of frame counts you will pass around byte counts.
1408 The 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
1409 managed buffers always being aligned to MA_SIMD_ALIGNMENT.
1411 Note that the ring buffer is only thread safe when used by a single consumer thread and single producer thread.
1417 The following backends are supported by miniaudio.
1419 +-------------+-----------------------+--------------------------------------------------------+
1420 | Name | Enum Name | Supported Operating Systems |
1421 +-------------+-----------------------+--------------------------------------------------------+
1422 | WASAPI | ma_backend_wasapi | Windows Vista+ |
1423 | DirectSound | ma_backend_dsound | Windows XP+ |
1424 | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) |
1425 | Core Audio | ma_backend_coreaudio | macOS, iOS |
1426 | ALSA | ma_backend_alsa | Linux |
1427 | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
1428 | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
1429 | sndio | ma_backend_sndio | OpenBSD |
1430 | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
1431 | OSS | ma_backend_oss | FreeBSD |
1432 | AAudio | ma_backend_aaudio | Android 8+ |
1433 | OpenSL ES | ma_backend_opensl | Android (API level 16+) |
1434 | Web Audio | ma_backend_webaudio | Web (via Emscripten) |
1435 | Custom | ma_backend_custom | Cross Platform |
1436 | Null | ma_backend_null | Cross Platform (not used on Web) |
1437 +-------------+-----------------------+--------------------------------------------------------+
1439 Some backends have some nuance details you may want to be aware of.
1443 - 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
1444 this, set `wasapi.noAutoConvertSRC` to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing when the
1445 `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC will result in miniaudio's internal resampler being used instead
1446 which will in turn enable the use of low-latency shared mode.
1450 - If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki:
1451 https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling. Alternatively, consider using a different backend such as ALSA.
1455 - To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: `<uses-permission android:name="android.permission.RECORD_AUDIO" />`
1456 - With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a limitation with OpenSL|ES.
1457 - 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
1458 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().
1459 - 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
1460 potential device-specific optimizations the driver may implement.
1464 - UWP only supports default playback and capture devices.
1465 - UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest):
1471 <DeviceCapability Name="microphone" />
1476 11.5. Web Audio / Emscripten
1477 ----------------------------
1478 - You cannot use `-std=c*` compiler flags, nor `-ansi`. This only applies to the Emscripten build.
1479 - 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.
1480 - Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as they've been deprecated.
1481 - 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
1482 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
1483 without first handling some kind of user input.
1487 12. Miscellaneous Notes
1488 =======================
1489 - Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for WASAPI and Core Audio, however other backends such as
1490 PulseAudio may naturally support it, though not all have been tested.
1491 - The contents of the output buffer passed into the data callback will always be pre-initialized to silence unless the `noPreZeroedOutputBuffer` config variable
1492 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.
1493 - 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
1494 clipping yourself, you can disable this overhead by setting `noClip` to true in the device config.
1495 - The sndio backend is currently only enabled on OpenBSD builds.
1496 - The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can use it.
1497 - Note that GCC and Clang requires `-msse2`, `-mavx2`, etc. for SIMD optimizations.
1498 - 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.
1508 #define MA_STRINGIFY(x) #x
1509 #define MA_XSTRINGIFY(x) MA_STRINGIFY(x)
1511 #define MA_VERSION_MAJOR 0
1512 #define MA_VERSION_MINOR 10
1513 #define MA_VERSION_REVISION 33
1514 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION)
1516 #if defined(_MSC_VER) && !defined(__clang__)
1517 #pragma warning(push)
1518 #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
1519 #pragma warning(disable:4214) /* nonstandard extension used: bit field types other than int */
1520 #pragma warning(disable:4324) /* structure was padded due to alignment specifier */
1521 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
1522 #pragma GCC diagnostic push
1523 #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
1524 #if defined(__clang__)
1525 #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */
1529 /* Platform/backend detection. */
1532 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP || WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
1533 #define MA_WIN32_UWP
1535 #define MA_WIN32_DESKTOP
1539 #include <pthread.h> /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */
1543 #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
1556 #ifdef __EMSCRIPTEN__
1557 #define MA_EMSCRIPTEN
1561 #include <stddef.h> /* For size_t. */
1564 typedef signed char ma_int8;
1565 typedef unsigned char ma_uint8;
1566 typedef signed short ma_int16;
1567 typedef unsigned short ma_uint16;
1568 typedef signed int ma_int32;
1569 typedef unsigned int ma_uint32;
1570 #if defined(_MSC_VER)
1571 typedef signed __int64 ma_int64;
1572 typedef unsigned __int64 ma_uint64;
1574 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
1575 #pragma GCC diagnostic push
1576 #pragma GCC diagnostic ignored "-Wlong-long"
1577 #if defined(__clang__)
1578 #pragma GCC diagnostic ignored "-Wc++11-long-long"
1581 typedef signed long long ma_int64;
1582 typedef unsigned long long ma_uint64;
1583 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
1584 #pragma GCC diagnostic pop
1587 #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
1588 typedef ma_uint64 ma_uintptr;
1590 typedef ma_uint32 ma_uintptr;
1593 typedef ma_uint8 ma_bool8;
1594 typedef ma_uint32 ma_bool32;
1598 typedef void* ma_handle;
1599 typedef void* ma_ptr;
1600 typedef void (* ma_proc)(void);
1602 #if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED)
1603 typedef ma_uint16 wchar_t;
1606 /* Define NULL for some compilers. */
1611 #if defined(SIZE_MAX)
1612 #define MA_SIZE_MAX SIZE_MAX
1614 #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */
1619 #define MA_INLINE __forceinline
1620 #elif defined(__GNUC__)
1622 I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
1623 the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
1624 case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
1625 command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
1626 I am using "__inline__" only when we're compiling in strict ANSI mode.
1628 #if defined(__STRICT_ANSI__)
1629 #define MA_INLINE __inline__ __attribute__((always_inline))
1631 #define MA_INLINE inline __attribute__((always_inline))
1633 #elif defined(__WATCOMC__)
1634 #define MA_INLINE __inline
1639 #if !defined(MA_API)
1642 #define MA_DLL_IMPORT __declspec(dllimport)
1643 #define MA_DLL_EXPORT __declspec(dllexport)
1644 #define MA_DLL_PRIVATE static
1646 #if defined(__GNUC__) && __GNUC__ >= 4
1647 #define MA_DLL_IMPORT __attribute__((visibility("default")))
1648 #define MA_DLL_EXPORT __attribute__((visibility("default")))
1649 #define MA_DLL_PRIVATE __attribute__((visibility("hidden")))
1651 #define MA_DLL_IMPORT
1652 #define MA_DLL_EXPORT
1653 #define MA_DLL_PRIVATE static
1657 #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
1658 #define MA_API MA_DLL_EXPORT
1660 #define MA_API MA_DLL_IMPORT
1662 #define MA_PRIVATE MA_DLL_PRIVATE
1664 #define MA_API extern
1665 #define MA_PRIVATE static
1669 /* SIMD alignment in bytes. Currently set to 64 bytes in preparation for future AVX-512 optimizations. */
1670 #define MA_SIMD_ALIGNMENT 64
1676 A log level will automatically include the lower levels. For example, verbose logging will enable everything. The warning log level will only include warnings
1677 and errors, but will ignore informational and verbose logging. If you only want to handle a specific log level, implement a custom log callback (see
1678 ma_context_init() for details) and interrogate the `logLevel` parameter.
1680 By default the log level will be set to MA_LOG_LEVEL_ERROR, but you can change this by defining MA_LOG_LEVEL before the implementation of miniaudio.
1682 MA_LOG_LEVEL_VERBOSE
1683 Mainly intended for debugging. This will enable all log levels and can be triggered from within the data callback so care must be taken when enabling this
1684 in production environments.
1687 Informational logging. Useful for debugging. This will also enable warning and error logs. This will never be called from within the data callback.
1689 MA_LOG_LEVEL_WARNING
1690 Warnings. You should enable this in you development builds and action them when encounted. This will also enable error logs. These logs usually indicate a
1691 potential problem or misconfiguration, but still allow you to keep running. This will never be called from within the data callback.
1694 Error logging. This will be fired when an operation fails and is subsequently aborted. This can be fired from within the data callback, in which case the
1695 device will be stopped. You should always have this log level enabled.
1697 #define MA_LOG_LEVEL_VERBOSE 4
1698 #define MA_LOG_LEVEL_INFO 3
1699 #define MA_LOG_LEVEL_WARNING 2
1700 #define MA_LOG_LEVEL_ERROR 1
1702 #ifndef MA_LOG_LEVEL
1703 #define MA_LOG_LEVEL MA_LOG_LEVEL_ERROR
1707 An annotation for variables which must be used atomically. This doesn't actually do anything - it's
1708 just used as a way for humans to identify variables that should be used atomically.
1712 typedef struct ma_context ma_context;
1713 typedef struct ma_device ma_device;
1715 typedef ma_uint8 ma_channel;
1716 #define MA_CHANNEL_NONE 0
1717 #define MA_CHANNEL_MONO 1
1718 #define MA_CHANNEL_FRONT_LEFT 2
1719 #define MA_CHANNEL_FRONT_RIGHT 3
1720 #define MA_CHANNEL_FRONT_CENTER 4
1721 #define MA_CHANNEL_LFE 5
1722 #define MA_CHANNEL_BACK_LEFT 6
1723 #define MA_CHANNEL_BACK_RIGHT 7
1724 #define MA_CHANNEL_FRONT_LEFT_CENTER 8
1725 #define MA_CHANNEL_FRONT_RIGHT_CENTER 9
1726 #define MA_CHANNEL_BACK_CENTER 10
1727 #define MA_CHANNEL_SIDE_LEFT 11
1728 #define MA_CHANNEL_SIDE_RIGHT 12
1729 #define MA_CHANNEL_TOP_CENTER 13
1730 #define MA_CHANNEL_TOP_FRONT_LEFT 14
1731 #define MA_CHANNEL_TOP_FRONT_CENTER 15
1732 #define MA_CHANNEL_TOP_FRONT_RIGHT 16
1733 #define MA_CHANNEL_TOP_BACK_LEFT 17
1734 #define MA_CHANNEL_TOP_BACK_CENTER 18
1735 #define MA_CHANNEL_TOP_BACK_RIGHT 19
1736 #define MA_CHANNEL_AUX_0 20
1737 #define MA_CHANNEL_AUX_1 21
1738 #define MA_CHANNEL_AUX_2 22
1739 #define MA_CHANNEL_AUX_3 23
1740 #define MA_CHANNEL_AUX_4 24
1741 #define MA_CHANNEL_AUX_5 25
1742 #define MA_CHANNEL_AUX_6 26
1743 #define MA_CHANNEL_AUX_7 27
1744 #define MA_CHANNEL_AUX_8 28
1745 #define MA_CHANNEL_AUX_9 29
1746 #define MA_CHANNEL_AUX_10 30
1747 #define MA_CHANNEL_AUX_11 31
1748 #define MA_CHANNEL_AUX_12 32
1749 #define MA_CHANNEL_AUX_13 33
1750 #define MA_CHANNEL_AUX_14 34
1751 #define MA_CHANNEL_AUX_15 35
1752 #define MA_CHANNEL_AUX_16 36
1753 #define MA_CHANNEL_AUX_17 37
1754 #define MA_CHANNEL_AUX_18 38
1755 #define MA_CHANNEL_AUX_19 39
1756 #define MA_CHANNEL_AUX_20 40
1757 #define MA_CHANNEL_AUX_21 41
1758 #define MA_CHANNEL_AUX_22 42
1759 #define MA_CHANNEL_AUX_23 43
1760 #define MA_CHANNEL_AUX_24 44
1761 #define MA_CHANNEL_AUX_25 45
1762 #define MA_CHANNEL_AUX_26 46
1763 #define MA_CHANNEL_AUX_27 47
1764 #define MA_CHANNEL_AUX_28 48
1765 #define MA_CHANNEL_AUX_29 49
1766 #define MA_CHANNEL_AUX_30 50
1767 #define MA_CHANNEL_AUX_31 51
1768 #define MA_CHANNEL_LEFT MA_CHANNEL_FRONT_LEFT
1769 #define MA_CHANNEL_RIGHT MA_CHANNEL_FRONT_RIGHT
1770 #define MA_CHANNEL_POSITION_COUNT (MA_CHANNEL_AUX_31 + 1)
1773 typedef int ma_result;
1774 #define MA_SUCCESS 0
1775 #define MA_ERROR -1 /* A generic error. */
1776 #define MA_INVALID_ARGS -2
1777 #define MA_INVALID_OPERATION -3
1778 #define MA_OUT_OF_MEMORY -4
1779 #define MA_OUT_OF_RANGE -5
1780 #define MA_ACCESS_DENIED -6
1781 #define MA_DOES_NOT_EXIST -7
1782 #define MA_ALREADY_EXISTS -8
1783 #define MA_TOO_MANY_OPEN_FILES -9
1784 #define MA_INVALID_FILE -10
1785 #define MA_TOO_BIG -11
1786 #define MA_PATH_TOO_LONG -12
1787 #define MA_NAME_TOO_LONG -13
1788 #define MA_NOT_DIRECTORY -14
1789 #define MA_IS_DIRECTORY -15
1790 #define MA_DIRECTORY_NOT_EMPTY -16
1791 #define MA_END_OF_FILE -17
1792 #define MA_NO_SPACE -18
1794 #define MA_IO_ERROR -20
1795 #define MA_INTERRUPT -21
1796 #define MA_UNAVAILABLE -22
1797 #define MA_ALREADY_IN_USE -23
1798 #define MA_BAD_ADDRESS -24
1799 #define MA_BAD_SEEK -25
1800 #define MA_BAD_PIPE -26
1801 #define MA_DEADLOCK -27
1802 #define MA_TOO_MANY_LINKS -28
1803 #define MA_NOT_IMPLEMENTED -29
1804 #define MA_NO_MESSAGE -30
1805 #define MA_BAD_MESSAGE -31
1806 #define MA_NO_DATA_AVAILABLE -32
1807 #define MA_INVALID_DATA -33
1808 #define MA_TIMEOUT -34
1809 #define MA_NO_NETWORK -35
1810 #define MA_NOT_UNIQUE -36
1811 #define MA_NOT_SOCKET -37
1812 #define MA_NO_ADDRESS -38
1813 #define MA_BAD_PROTOCOL -39
1814 #define MA_PROTOCOL_UNAVAILABLE -40
1815 #define MA_PROTOCOL_NOT_SUPPORTED -41
1816 #define MA_PROTOCOL_FAMILY_NOT_SUPPORTED -42
1817 #define MA_ADDRESS_FAMILY_NOT_SUPPORTED -43
1818 #define MA_SOCKET_NOT_SUPPORTED -44
1819 #define MA_CONNECTION_RESET -45
1820 #define MA_ALREADY_CONNECTED -46
1821 #define MA_NOT_CONNECTED -47
1822 #define MA_CONNECTION_REFUSED -48
1823 #define MA_NO_HOST -49
1824 #define MA_IN_PROGRESS -50
1825 #define MA_CANCELLED -51
1826 #define MA_MEMORY_ALREADY_MAPPED -52
1827 #define MA_AT_END -53
1829 /* General miniaudio-specific errors. */
1830 #define MA_FORMAT_NOT_SUPPORTED -100
1831 #define MA_DEVICE_TYPE_NOT_SUPPORTED -101
1832 #define MA_SHARE_MODE_NOT_SUPPORTED -102
1833 #define MA_NO_BACKEND -103
1834 #define MA_NO_DEVICE -104
1835 #define MA_API_NOT_FOUND -105
1836 #define MA_INVALID_DEVICE_CONFIG -106
1837 #define MA_LOOP -107
1840 #define MA_DEVICE_NOT_INITIALIZED -200
1841 #define MA_DEVICE_ALREADY_INITIALIZED -201
1842 #define MA_DEVICE_NOT_STARTED -202
1843 #define MA_DEVICE_NOT_STOPPED -203
1845 /* Operation errors. */
1846 #define MA_FAILED_TO_INIT_BACKEND -300
1847 #define MA_FAILED_TO_OPEN_BACKEND_DEVICE -301
1848 #define MA_FAILED_TO_START_BACKEND_DEVICE -302
1849 #define MA_FAILED_TO_STOP_BACKEND_DEVICE -303
1852 #define MA_MIN_CHANNELS 1
1853 #ifndef MA_MAX_CHANNELS
1854 #define MA_MAX_CHANNELS 32
1858 #ifndef MA_MAX_FILTER_ORDER
1859 #define MA_MAX_FILTER_ORDER 8
1864 ma_stream_format_pcm = 0
1869 ma_stream_layout_interleaved = 0,
1870 ma_stream_layout_deinterleaved
1875 ma_dither_mode_none = 0,
1876 ma_dither_mode_rectangle,
1877 ma_dither_mode_triangle
1883 I like to keep these explicitly defined because they're used as a key into a lookup table. When items are
1884 added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample().
1886 ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */
1888 ma_format_s16 = 2, /* Seems to be the most widely supported format. */
1889 ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */
1897 /* Standard rates need to be in priority order. */
1898 ma_standard_sample_rate_48000 = 48000, /* Most common */
1899 ma_standard_sample_rate_44100 = 44100,
1901 ma_standard_sample_rate_32000 = 32000, /* Lows */
1902 ma_standard_sample_rate_24000 = 24000,
1903 ma_standard_sample_rate_22050 = 22050,
1905 ma_standard_sample_rate_88200 = 88200, /* Highs */
1906 ma_standard_sample_rate_96000 = 96000,
1907 ma_standard_sample_rate_176400 = 176400,
1908 ma_standard_sample_rate_192000 = 192000,
1910 ma_standard_sample_rate_16000 = 16000, /* Extreme lows */
1911 ma_standard_sample_rate_11025 = 11250,
1912 ma_standard_sample_rate_8000 = 8000,
1914 ma_standard_sample_rate_352800 = 352800, /* Extreme highs */
1915 ma_standard_sample_rate_384000 = 384000,
1917 ma_standard_sample_rate_min = ma_standard_sample_rate_8000,
1918 ma_standard_sample_rate_max = ma_standard_sample_rate_384000,
1919 ma_standard_sample_rate_count = 14 /* Need to maintain the count manually. Make sure this is updated if items are added to enum. */
1920 } ma_standard_sample_rate;
1922 /* These are deprecated. Use ma_standard_sample_rate_min and ma_standard_sample_rate_max. */
1923 #define MA_MIN_SAMPLE_RATE (ma_uint32)ma_standard_sample_rate_min
1924 #define MA_MAX_SAMPLE_RATE (ma_uint32)ma_standard_sample_rate_max
1929 ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */
1930 ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */
1931 ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_router_config. */
1932 ma_channel_mix_mode_planar_blend = ma_channel_mix_mode_rectangular,
1933 ma_channel_mix_mode_default = ma_channel_mix_mode_planar_blend
1934 } ma_channel_mix_mode;
1938 ma_standard_channel_map_microsoft,
1939 ma_standard_channel_map_alsa,
1940 ma_standard_channel_map_rfc3551, /* Based off AIFF. */
1941 ma_standard_channel_map_flac,
1942 ma_standard_channel_map_vorbis,
1943 ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */
1944 ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */
1945 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. */
1946 ma_standard_channel_map_default = ma_standard_channel_map_microsoft
1947 } ma_standard_channel_map;
1951 ma_performance_profile_low_latency = 0,
1952 ma_performance_profile_conservative
1953 } ma_performance_profile;
1959 void* (* onMalloc)(size_t sz, void* pUserData);
1960 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
1961 void (* onFree)(void* p, void* pUserData);
1962 } ma_allocation_callbacks;
1970 #ifndef MA_NO_THREADING
1971 /* Thread priorities should be ordered such that the default priority of the worker thread is 0. */
1974 ma_thread_priority_idle = -5,
1975 ma_thread_priority_lowest = -4,
1976 ma_thread_priority_low = -3,
1977 ma_thread_priority_normal = -2,
1978 ma_thread_priority_high = -1,
1979 ma_thread_priority_highest = 0,
1980 ma_thread_priority_realtime = 1,
1981 ma_thread_priority_default = 0
1982 } ma_thread_priority;
1984 /* Spinlocks are 32-bit for compatibility reasons. */
1985 typedef ma_uint32 ma_spinlock;
1987 #if defined(MA_WIN32)
1988 typedef ma_handle ma_thread;
1990 #if defined(MA_POSIX)
1991 typedef pthread_t ma_thread;
1994 #if defined(MA_WIN32)
1995 typedef ma_handle ma_mutex;
1997 #if defined(MA_POSIX)
1998 typedef pthread_mutex_t ma_mutex;
2001 #if defined(MA_WIN32)
2002 typedef ma_handle ma_event;
2004 #if defined(MA_POSIX)
2008 pthread_mutex_t lock;
2009 pthread_cond_t cond;
2011 #endif /* MA_POSIX */
2013 #if defined(MA_WIN32)
2014 typedef ma_handle ma_semaphore;
2016 #if defined(MA_POSIX)
2020 pthread_mutex_t lock;
2021 pthread_cond_t cond;
2023 #endif /* MA_POSIX */
2025 /* 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. */
2026 #ifndef MA_NO_DEVICE_IO
2027 #error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO";
2029 #endif /* MA_NO_THREADING */
2033 Retrieves the version of miniaudio as separated integers. Each component can be NULL if it's not required.
2035 MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);
2038 Retrieves the version of miniaudio as a string which can be useful for logging purposes.
2040 MA_API const char* ma_version_string(void);
2043 /**************************************************************************************************************************************************************
2047 **************************************************************************************************************************************************************/
2052 } ma_biquad_coefficient;
2066 MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2);
2072 ma_biquad_coefficient b0;
2073 ma_biquad_coefficient b1;
2074 ma_biquad_coefficient b2;
2075 ma_biquad_coefficient a1;
2076 ma_biquad_coefficient a2;
2077 ma_biquad_coefficient r1[MA_MAX_CHANNELS];
2078 ma_biquad_coefficient r2[MA_MAX_CHANNELS];
2081 MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, ma_biquad* pBQ);
2082 MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ);
2083 MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2084 MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ);
2087 /**************************************************************************************************************************************************************
2091 **************************************************************************************************************************************************************/
2096 ma_uint32 sampleRate;
2097 double cutoffFrequency;
2099 } ma_lpf1_config, ma_lpf2_config;
2101 MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency);
2102 MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);
2108 ma_biquad_coefficient a;
2109 ma_biquad_coefficient r1[MA_MAX_CHANNELS];
2112 MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, ma_lpf1* pLPF);
2113 MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF);
2114 MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2115 MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF);
2119 ma_biquad bq; /* The second order low-pass filter is implemented as a biquad filter. */
2122 MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, ma_lpf2* pLPF);
2123 MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF);
2124 MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2125 MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF);
2132 ma_uint32 sampleRate;
2133 double cutoffFrequency;
2134 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
2137 MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
2143 ma_uint32 sampleRate;
2144 ma_uint32 lpf1Count;
2145 ma_uint32 lpf2Count;
2147 ma_lpf2 lpf2[MA_MAX_FILTER_ORDER/2];
2150 MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF);
2151 MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF);
2152 MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2153 MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF);
2156 /**************************************************************************************************************************************************************
2160 **************************************************************************************************************************************************************/
2165 ma_uint32 sampleRate;
2166 double cutoffFrequency;
2168 } ma_hpf1_config, ma_hpf2_config;
2170 MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency);
2171 MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);
2177 ma_biquad_coefficient a;
2178 ma_biquad_coefficient r1[MA_MAX_CHANNELS];
2181 MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, ma_hpf1* pHPF);
2182 MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF);
2183 MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2184 MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF);
2188 ma_biquad bq; /* The second order high-pass filter is implemented as a biquad filter. */
2191 MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, ma_hpf2* pHPF);
2192 MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF);
2193 MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2194 MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF);
2201 ma_uint32 sampleRate;
2202 double cutoffFrequency;
2203 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
2206 MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
2212 ma_uint32 sampleRate;
2213 ma_uint32 hpf1Count;
2214 ma_uint32 hpf2Count;
2216 ma_hpf2 hpf2[MA_MAX_FILTER_ORDER/2];
2219 MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, ma_hpf* pHPF);
2220 MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF);
2221 MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2222 MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF);
2225 /**************************************************************************************************************************************************************
2229 **************************************************************************************************************************************************************/
2234 ma_uint32 sampleRate;
2235 double cutoffFrequency;
2239 MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);
2243 ma_biquad bq; /* The second order band-pass filter is implemented as a biquad filter. */
2246 MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, ma_bpf2* pBPF);
2247 MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF);
2248 MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2249 MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF);
2256 ma_uint32 sampleRate;
2257 double cutoffFrequency;
2258 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
2261 MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
2267 ma_uint32 bpf2Count;
2268 ma_bpf2 bpf2[MA_MAX_FILTER_ORDER/2];
2271 MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, ma_bpf* pBPF);
2272 MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF);
2273 MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2274 MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF);
2277 /**************************************************************************************************************************************************************
2281 **************************************************************************************************************************************************************/
2286 ma_uint32 sampleRate;
2291 MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency);
2298 MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, ma_notch2* pFilter);
2299 MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter);
2300 MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2301 MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter);
2304 /**************************************************************************************************************************************************************
2308 **************************************************************************************************************************************************************/
2313 ma_uint32 sampleRate;
2319 MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
2326 MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, ma_peak2* pFilter);
2327 MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter);
2328 MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2329 MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter);
2332 /**************************************************************************************************************************************************************
2336 **************************************************************************************************************************************************************/
2341 ma_uint32 sampleRate;
2345 } ma_loshelf2_config;
2347 MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency);
2354 MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter);
2355 MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter);
2356 MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2357 MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter);
2360 /**************************************************************************************************************************************************************
2364 **************************************************************************************************************************************************************/
2369 ma_uint32 sampleRate;
2373 } ma_hishelf2_config;
2375 MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency);
2382 MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter);
2383 MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter);
2384 MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2385 MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter);
2389 /************************************************************************************************************************************************************
2390 *************************************************************************************************************************************************************
2395 This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc.
2397 *************************************************************************************************************************************************************
2398 ************************************************************************************************************************************************************/
2400 /**************************************************************************************************************************************************************
2404 **************************************************************************************************************************************************************/
2409 ma_uint32 sampleRateIn;
2410 ma_uint32 sampleRateOut;
2411 ma_uint32 lpfOrder; /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. */
2412 double lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */
2413 } ma_linear_resampler_config;
2415 MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
2419 ma_linear_resampler_config config;
2420 ma_uint32 inAdvanceInt;
2421 ma_uint32 inAdvanceFrac;
2422 ma_uint32 inTimeInt;
2423 ma_uint32 inTimeFrac;
2426 float f32[MA_MAX_CHANNELS];
2427 ma_int16 s16[MA_MAX_CHANNELS];
2428 } x0; /* The previous input frame. */
2431 float f32[MA_MAX_CHANNELS];
2432 ma_int16 s16[MA_MAX_CHANNELS];
2433 } x1; /* The next input frame. */
2435 } ma_linear_resampler;
2437 MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, ma_linear_resampler* pResampler);
2438 MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler);
2439 MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
2440 MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
2441 MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut);
2442 MA_API ma_uint64 ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount);
2443 MA_API ma_uint64 ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount);
2444 MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler);
2445 MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler);
2449 ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */
2450 ma_resample_algorithm_speex
2451 } ma_resample_algorithm;
2455 ma_format format; /* Must be either ma_format_f32 or ma_format_s16. */
2457 ma_uint32 sampleRateIn;
2458 ma_uint32 sampleRateOut;
2459 ma_resample_algorithm algorithm;
2463 double lpfNyquistFactor;
2467 int quality; /* 0 to 10. Defaults to 3. */
2469 } ma_resampler_config;
2471 MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm);
2475 ma_resampler_config config;
2478 ma_linear_resampler linear;
2481 void* pSpeexResamplerState; /* SpeexResamplerState* */
2487 Initializes a new resampler object from a config.
2489 MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pResampler);
2492 Uninitializes a resampler.
2494 MA_API void ma_resampler_uninit(ma_resampler* pResampler);
2497 Converts the given input data.
2499 Both the input and output frames must be in the format specified in the config when the resampler was initilized.
2501 On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that
2502 were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use
2503 ma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames.
2505 On input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole
2506 input frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames
2507 you should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead.
2509 If [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of
2510 output frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input
2511 frames. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be
2512 processed. In this case, any internal filter state will be updated as if zeroes were passed in.
2514 It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL.
2516 It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL.
2518 MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
2522 Sets the input and output sample sample rate.
2524 MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
2527 Sets the input and output sample rate as a ratio.
2529 The ration is in/out.
2531 MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio);
2535 Calculates the number of whole input frames that would need to be read from the client in order to output the specified
2536 number of output frames.
2538 The returned value does not include cached input frames. It only returns the number of extra frames that would need to be
2539 read from the input buffer in order to output the specified number of output frames.
2541 MA_API ma_uint64 ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount);
2544 Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of
2547 MA_API ma_uint64 ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount);
2551 Retrieves the latency introduced by the resampler in input frames.
2553 MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler);
2556 Retrieves the latency introduced by the resampler in output frames.
2558 MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler);
2562 /**************************************************************************************************************************************************************
2566 **************************************************************************************************************************************************************/
2570 ma_uint32 channelsIn;
2571 ma_uint32 channelsOut;
2572 ma_channel channelMapIn[MA_MAX_CHANNELS];
2573 ma_channel channelMapOut[MA_MAX_CHANNELS];
2574 ma_channel_mix_mode mixingMode;
2575 float weights[MA_MAX_CHANNELS][MA_MAX_CHANNELS]; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */
2576 } ma_channel_converter_config;
2578 MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode);
2583 ma_uint32 channelsIn;
2584 ma_uint32 channelsOut;
2585 ma_channel channelMapIn[MA_MAX_CHANNELS];
2586 ma_channel channelMapOut[MA_MAX_CHANNELS];
2587 ma_channel_mix_mode mixingMode;
2590 float f32[MA_MAX_CHANNELS][MA_MAX_CHANNELS];
2591 ma_int32 s16[MA_MAX_CHANNELS][MA_MAX_CHANNELS];
2593 ma_bool8 isPassthrough;
2594 ma_bool8 isSimpleShuffle;
2595 ma_bool8 isSimpleMonoExpansion;
2596 ma_bool8 isStereoToMono;
2597 ma_uint8 shuffleTable[MA_MAX_CHANNELS];
2598 } ma_channel_converter;
2600 MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, ma_channel_converter* pConverter);
2601 MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter);
2602 MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2605 /**************************************************************************************************************************************************************
2609 **************************************************************************************************************************************************************/
2613 ma_format formatOut;
2614 ma_uint32 channelsIn;
2615 ma_uint32 channelsOut;
2616 ma_uint32 sampleRateIn;
2617 ma_uint32 sampleRateOut;
2618 ma_channel channelMapIn[MA_MAX_CHANNELS];
2619 ma_channel channelMapOut[MA_MAX_CHANNELS];
2620 ma_dither_mode ditherMode;
2621 ma_channel_mix_mode channelMixMode;
2622 float channelWeights[MA_MAX_CHANNELS][MA_MAX_CHANNELS]; /* [in][out]. Only used when channelMixMode is set to ma_channel_mix_mode_custom_weights. */
2625 ma_resample_algorithm algorithm;
2626 ma_bool32 allowDynamicSampleRate;
2630 double lpfNyquistFactor;
2637 } ma_data_converter_config;
2639 MA_API ma_data_converter_config ma_data_converter_config_init_default(void);
2640 MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
2644 ma_data_converter_config config;
2645 ma_channel_converter channelConverter;
2646 ma_resampler resampler;
2647 ma_bool8 hasPreFormatConversion;
2648 ma_bool8 hasPostFormatConversion;
2649 ma_bool8 hasChannelConverter;
2650 ma_bool8 hasResampler;
2651 ma_bool8 isPassthrough;
2652 } ma_data_converter;
2654 MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, ma_data_converter* pConverter);
2655 MA_API void ma_data_converter_uninit(ma_data_converter* pConverter);
2656 MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
2657 MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
2658 MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut);
2659 MA_API ma_uint64 ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount);
2660 MA_API ma_uint64 ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount);
2661 MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter);
2662 MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter);
2665 /************************************************************************************************************************************************************
2669 ************************************************************************************************************************************************************/
2670 MA_API void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2671 MA_API void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2672 MA_API void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2673 MA_API void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2674 MA_API void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2675 MA_API void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2676 MA_API void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2677 MA_API void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2678 MA_API void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2679 MA_API void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2680 MA_API void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2681 MA_API void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2682 MA_API void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2683 MA_API void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2684 MA_API void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2685 MA_API void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2686 MA_API void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2687 MA_API void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2688 MA_API void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2689 MA_API void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2690 MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode);
2691 MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode);
2694 Deinterleaves an interleaved buffer.
2696 MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames);
2699 Interleaves a group of deinterleaved buffers.
2701 MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames);
2704 /************************************************************************************************************************************************************
2708 ************************************************************************************************************************************************************/
2711 Initializes a blank channel map.
2713 When a blank channel map is specified anywhere it indicates that the native channel map should be used.
2715 MA_API void ma_channel_map_init_blank(ma_uint32 channels, ma_channel* pChannelMap);
2718 Helper for retrieving a standard channel map.
2720 The output channel map buffer must have a capacity of at least `channels`.
2722 MA_API void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel* pChannelMap);
2725 Copies a channel map.
2727 Both input and output channel map buffers must have a capacity of at at least `channels`.
2729 MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels);
2732 Copies a channel map if one is specified, otherwise copies the default channel map.
2734 The output buffer must have a capacity of at least `channels`. If not NULL, the input channel map must also have a capacity of at least `channels`.
2736 MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels);
2740 Determines whether or not a channel map is valid.
2742 A blank channel map is valid (all channels set to MA_CHANNEL_NONE). The way a blank channel map is handled is context specific, but
2743 is usually treated as a passthrough.
2745 Invalid channel maps:
2746 - A channel map with no channels
2747 - A channel map with more than one channel and a mono channel
2749 The channel map buffer must have a capacity of at least `channels`.
2751 MA_API ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel* pChannelMap);
2754 Helper for comparing two channel maps for equality.
2756 This assumes the channel count is the same between the two.
2758 Both channels map buffers must have a capacity of at least `channels`.
2760 MA_API ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel* pChannelMapA, const ma_channel* pChannelMapB);
2763 Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE).
2765 The channel map buffer must have a capacity of at least `channels`.
2767 MA_API ma_bool32 ma_channel_map_blank(ma_uint32 channels, const ma_channel* pChannelMap);
2770 Helper for determining whether or not a channel is present in the given channel map.
2772 The channel map buffer must have a capacity of at least `channels`.
2774 MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition);
2777 /************************************************************************************************************************************************************
2781 ************************************************************************************************************************************************************/
2784 High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to
2785 determine the required size of the output buffer. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is
2788 A return value of 0 indicates an error.
2790 This function is useful for one-off bulk conversions, but if you're streaming data you should use the ma_data_converter APIs instead.
2792 MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn);
2793 MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig);
2796 /************************************************************************************************************************************************************
2800 ************************************************************************************************************************************************************/
2804 ma_uint32 subbufferSizeInBytes;
2805 ma_uint32 subbufferCount;
2806 ma_uint32 subbufferStrideInBytes;
2807 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. */
2808 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. */
2809 ma_bool8 ownsBuffer; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */
2810 ma_bool8 clearOnWriteAcquire; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */
2811 ma_allocation_callbacks allocationCallbacks;
2814 MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB);
2815 MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB);
2816 MA_API void ma_rb_uninit(ma_rb* pRB);
2817 MA_API void ma_rb_reset(ma_rb* pRB);
2818 MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
2819 MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut);
2820 MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
2821 MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut);
2822 MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes);
2823 MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes);
2824 MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB); /* Returns the distance between the write pointer and the read pointer. Should never be negative for a correct program. Will return the number of bytes that can be read before the read pointer hits the write pointer. */
2825 MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB);
2826 MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB);
2827 MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB);
2828 MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB);
2829 MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex);
2830 MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer);
2840 MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB);
2841 MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB);
2842 MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB);
2843 MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB);
2844 MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
2845 MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut);
2846 MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
2847 MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut);
2848 MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames);
2849 MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames);
2850 MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB); /* Return value is in frames. */
2851 MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB);
2852 MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB);
2853 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB);
2854 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB);
2855 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex);
2856 MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer);
2860 The idea of the duplex ring buffer is to act as the intermediary buffer when running two asynchronous devices in a duplex set up. The
2861 capture device writes to it, and then a playback device reads from it.
2863 At the moment this is just a simple naive implementation, but in the future I want to implement some dynamic resampling to seamlessly
2864 handle desyncs. Note that the API is work in progress and may change at any time in any version.
2866 The size of the buffer is based on the capture side since that's what'll be written to the buffer. It is based on the capture period size
2867 in frames. The internal sample rate of the capture device is also needed in order to calculate the size.
2874 MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB);
2875 MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB);
2878 /************************************************************************************************************************************************************
2880 Miscellaneous Helpers
2882 ************************************************************************************************************************************************************/
2884 Retrieves a human readable description of the given result code.
2886 MA_API const char* ma_result_description(ma_result result);
2889 malloc(). Calls MA_MALLOC().
2891 MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
2894 realloc(). Calls MA_REALLOC().
2896 MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
2899 free(). Calls MA_FREE().
2901 MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
2904 Performs an aligned malloc, with the assumption that the alignment is a power of 2.
2906 MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks);
2909 Free's an aligned malloc'd buffer.
2911 MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
2914 Retrieves a friendly name for a format.
2916 MA_API const char* ma_get_format_name(ma_format format);
2919 Blends two frames in floating point format.
2921 MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels);
2924 Retrieves the size of a sample in bytes for the given format.
2926 This API is efficient and is implemented using a lookup table.
2931 MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format);
2932 static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; }
2935 Converts a log level to a string.
2937 MA_API const char* ma_log_level_to_string(ma_uint32 logLevel);
2941 /************************************************************************************************************************************************************
2942 *************************************************************************************************************************************************************
2947 This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc.
2949 *************************************************************************************************************************************************************
2950 ************************************************************************************************************************************************************/
2951 #ifndef MA_NO_DEVICE_IO
2952 /* Some backends are only supported on certain platforms. */
2953 #if defined(MA_WIN32)
2954 #define MA_SUPPORT_WASAPI
2955 #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */
2956 #define MA_SUPPORT_DSOUND
2957 #define MA_SUPPORT_WINMM
2958 #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */
2961 #if defined(MA_UNIX)
2962 #if defined(MA_LINUX)
2963 #if !defined(MA_ANDROID) /* ALSA is not supported on Android. */
2964 #define MA_SUPPORT_ALSA
2967 #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN)
2968 #define MA_SUPPORT_PULSEAUDIO
2969 #define MA_SUPPORT_JACK
2971 #if defined(MA_ANDROID)
2972 #define MA_SUPPORT_AAUDIO
2973 #define MA_SUPPORT_OPENSL
2975 #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */
2976 #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */
2978 #if defined(__NetBSD__) || defined(__OpenBSD__)
2979 #define MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */
2981 #if defined(__FreeBSD__) || defined(__DragonFly__)
2982 #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */
2985 #if defined(MA_APPLE)
2986 #define MA_SUPPORT_COREAUDIO
2988 #if defined(MA_EMSCRIPTEN)
2989 #define MA_SUPPORT_WEBAUDIO
2992 /* All platforms should support custom backends. */
2993 #define MA_SUPPORT_CUSTOM
2995 /* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */
2996 #if !defined(MA_EMSCRIPTEN)
2997 #define MA_SUPPORT_NULL
3001 #if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI))
3002 #define MA_HAS_WASAPI
3004 #if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND))
3005 #define MA_HAS_DSOUND
3007 #if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM))
3008 #define MA_HAS_WINMM
3010 #if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA))
3013 #if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO))
3014 #define MA_HAS_PULSEAUDIO
3016 #if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK))
3019 #if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO))
3020 #define MA_HAS_COREAUDIO
3022 #if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO))
3023 #define MA_HAS_SNDIO
3025 #if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4))
3026 #define MA_HAS_AUDIO4
3028 #if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS))
3031 #if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO))
3032 #define MA_HAS_AAUDIO
3034 #if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL))
3035 #define MA_HAS_OPENSL
3037 #if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO))
3038 #define MA_HAS_WEBAUDIO
3040 #if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM))
3041 #define MA_HAS_CUSTOM
3043 #if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL))
3047 #define MA_STATE_UNINITIALIZED 0
3048 #define MA_STATE_STOPPED 1 /* The device's default state after initialization. */
3049 #define MA_STATE_STARTED 2 /* The device is started and is requesting and/or delivering audio data. */
3050 #define MA_STATE_STARTING 3 /* Transitioning from a stopped state to started. */
3051 #define MA_STATE_STOPPING 4 /* Transitioning from a started state to stopped. */
3053 #ifdef MA_SUPPORT_WASAPI
3054 /* We need a IMMNotificationClient object for WASAPI. */
3060 } ma_IMMNotificationClient;
3063 /* Backend enums must be in priority order. */
3069 ma_backend_coreaudio,
3073 ma_backend_pulseaudio,
3078 ma_backend_webaudio,
3079 ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */
3080 ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */
3083 #define MA_BACKEND_COUNT (ma_backend_null+1)
3087 The callback for processing audio data from the device.
3089 The data callback is fired by miniaudio whenever the device needs to have more data delivered to a playback device, or when a capture device has some data
3090 available. This is called as soon as the backend asks for more data which means it may be called with inconsistent frame counts. You cannot assume the
3091 callback will be fired with a consistent frame count.
3097 A pointer to the relevant device.
3100 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
3101 full-duplex device and null for a capture and loopback device.
3104 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
3108 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
3109 `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must
3110 not assume this will always be the same value each time the callback is fired.
3115 You cannot stop and start the device from inside the callback or else you'll get a deadlock. You must also not uninitialize the device from inside the
3116 callback. The following APIs cannot be called from inside the callback:
3124 The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread.
3126 typedef void (* ma_device_callback_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
3129 The callback for when the device has been stopped.
3131 This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces
3132 such as being unplugged or an internal error occuring.
3138 A pointer to the device that has just stopped.
3143 Do not restart or uninitialize the device from the callback.
3145 typedef void (* ma_stop_proc)(ma_device* pDevice);
3148 The callback for handling log messages.
3154 A pointer to the context the log message originated from.
3157 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.
3160 The log level. This can be one of the following:
3162 +----------------------+
3164 +----------------------+
3165 | MA_LOG_LEVEL_VERBOSE |
3166 | MA_LOG_LEVEL_INFO |
3167 | MA_LOG_LEVEL_WARNING |
3168 | MA_LOG_LEVEL_ERROR |
3169 +----------------------+
3177 Do not modify the state of the device from inside the callback.
3179 typedef void (* ma_log_proc)(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message);
3183 ma_device_type_playback = 1,
3184 ma_device_type_capture = 2,
3185 ma_device_type_duplex = ma_device_type_playback | ma_device_type_capture, /* 3 */
3186 ma_device_type_loopback = 4
3191 ma_share_mode_shared = 0,
3192 ma_share_mode_exclusive
3195 /* iOS/tvOS/watchOS session categories. */
3198 ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord with AVAudioSessionCategoryOptionDefaultToSpeaker. */
3199 ma_ios_session_category_none, /* Leave the session category unchanged. */
3200 ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */
3201 ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */
3202 ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */
3203 ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */
3204 ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */
3205 ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */
3206 } ma_ios_session_category;
3208 /* iOS/tvOS/watchOS session category options */
3211 ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */
3212 ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */
3213 ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */
3214 ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */
3215 ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */
3216 ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */
3217 ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */
3218 } ma_ios_session_category_option;
3220 /* OpenSL stream types. */
3223 ma_opensl_stream_type_default = 0, /* Leaves the stream type unset. */
3224 ma_opensl_stream_type_voice, /* SL_ANDROID_STREAM_VOICE */
3225 ma_opensl_stream_type_system, /* SL_ANDROID_STREAM_SYSTEM */
3226 ma_opensl_stream_type_ring, /* SL_ANDROID_STREAM_RING */
3227 ma_opensl_stream_type_media, /* SL_ANDROID_STREAM_MEDIA */
3228 ma_opensl_stream_type_alarm, /* SL_ANDROID_STREAM_ALARM */
3229 ma_opensl_stream_type_notification /* SL_ANDROID_STREAM_NOTIFICATION */
3230 } ma_opensl_stream_type;
3232 /* OpenSL recording presets. */
3235 ma_opensl_recording_preset_default = 0, /* Leaves the input preset unset. */
3236 ma_opensl_recording_preset_generic, /* SL_ANDROID_RECORDING_PRESET_GENERIC */
3237 ma_opensl_recording_preset_camcorder, /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */
3238 ma_opensl_recording_preset_voice_recognition, /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */
3239 ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */
3240 ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */
3241 } ma_opensl_recording_preset;
3243 /* AAudio usage types. */
3246 ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */
3247 ma_aaudio_usage_announcement, /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */
3248 ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */
3249 ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */
3250 ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */
3251 ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */
3252 ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */
3253 ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */
3254 ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */
3255 ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */
3256 ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */
3257 ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */
3258 ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */
3259 ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */
3260 ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */
3261 ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */
3262 ma_aaudio_usage_voice_communication_signalling /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */
3265 /* AAudio content types. */
3268 ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */
3269 ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */
3270 ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */
3271 ma_aaudio_content_type_sonification, /* AAUDIO_CONTENT_TYPE_SONIFICATION */
3272 ma_aaudio_content_type_speech /* AAUDIO_CONTENT_TYPE_SPEECH */
3273 } ma_aaudio_content_type;
3275 /* AAudio input presets. */
3278 ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */
3279 ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */
3280 ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */
3281 ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */
3282 ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */
3283 ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */
3284 ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */
3285 } ma_aaudio_input_preset;
3296 wchar_t wasapi[64]; /* WASAPI uses a wchar_t string for identification. */
3297 ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */
3298 /*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. */
3299 char alsa[256]; /* ALSA uses a name string for identification. */
3300 char pulse[256]; /* PulseAudio uses a name string for identification. */
3301 int jack; /* JACK always uses default devices. */
3302 char coreaudio[256]; /* Core Audio uses a string for identification. */
3303 char sndio[256]; /* "snd/0", etc. */
3304 char audio4[256]; /* "/dev/audio", etc. */
3305 char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */
3306 ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */
3307 ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */
3308 char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */
3314 } custom; /* The custom backend could be anything. Give them a few options. */
3315 int nullbackend; /* The null backend uses an integer for device IDs. */
3319 typedef struct ma_context_config ma_context_config;
3320 typedef struct ma_device_config ma_device_config;
3321 typedef struct ma_backend_callbacks ma_backend_callbacks;
3323 #define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */
3327 /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */
3330 ma_bool32 isDefault;
3333 Detailed info. As much of this is filled as possible with ma_context_get_device_info(). Note that you are allowed to initialize
3334 a device with settings outside of this range, but it just means the data will be converted using miniaudio's data conversion
3335 pipeline before sending the data to/from the device. Most programs will need to not worry about these values, but it's provided
3336 here mainly for informational purposes or in the rare case that someone might find it useful.
3338 These will be set to 0 when returned by ma_context_enumerate_devices() or ma_context_get_devices().
3340 ma_uint32 formatCount;
3341 ma_format formats[ma_format_count];
3342 ma_uint32 minChannels;
3343 ma_uint32 maxChannels;
3344 ma_uint32 minSampleRate;
3345 ma_uint32 maxSampleRate;
3348 /* Experimental. Don't use these right now. */
3349 ma_uint32 nativeDataFormatCount;
3352 ma_format format; /* Sample format. If set to ma_format_unknown, all sample formats are supported. */
3353 ma_uint32 channels; /* If set to 0, all channels are supported. */
3354 ma_uint32 sampleRate; /* If set to 0, all sample rates are supported. */
3355 ma_uint32 flags; /* A combination of MA_DATA_FORMAT_FLAG_* flags. */
3356 } 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. */
3359 struct ma_device_config
3361 ma_device_type deviceType;
3362 ma_uint32 sampleRate;
3363 ma_uint32 periodSizeInFrames;
3364 ma_uint32 periodSizeInMilliseconds;
3366 ma_performance_profile performanceProfile;
3367 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. */
3368 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. */
3369 ma_device_callback_proc dataCallback;
3370 ma_stop_proc stopCallback;
3374 ma_resample_algorithm algorithm;
3386 const ma_device_id* pDeviceID;
3389 ma_channel channelMap[MA_MAX_CHANNELS];
3390 ma_channel_mix_mode channelMixMode;
3391 ma_share_mode shareMode;
3395 const ma_device_id* pDeviceID;
3398 ma_channel channelMap[MA_MAX_CHANNELS];
3399 ma_channel_mix_mode channelMixMode;
3400 ma_share_mode shareMode;
3405 ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
3406 ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
3407 ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */
3408 ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */
3412 ma_bool32 noMMap; /* Disables MMap mode. */
3413 ma_bool32 noAutoFormat; /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */
3414 ma_bool32 noAutoChannels; /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */
3415 ma_bool32 noAutoResample; /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */
3419 const char* pStreamNamePlayback;
3420 const char* pStreamNameCapture;
3424 ma_bool32 allowNominalSampleRateChange; /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */
3428 ma_opensl_stream_type streamType;
3429 ma_opensl_recording_preset recordingPreset;
3433 ma_aaudio_usage usage;
3434 ma_aaudio_content_type contentType;
3435 ma_aaudio_input_preset inputPreset;
3441 The callback for handling device enumeration. This is fired from `ma_context_enumerated_devices()`.
3447 A pointer to the context performing the enumeration.
3450 The type of the device being enumerated. This will always be either `ma_device_type_playback` or `ma_device_type_capture`.
3453 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,
3454 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
3458 The user data pointer passed into `ma_context_enumerate_devices()`.
3460 typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData);
3464 Describes some basic details about a playback or capture device.
3468 const ma_device_id* pDeviceID;
3469 ma_share_mode shareMode;
3472 ma_uint32 sampleRate;
3473 ma_channel channelMap[MA_MAX_CHANNELS];
3474 ma_uint32 periodSizeInFrames;
3475 ma_uint32 periodSizeInMilliseconds;
3476 ma_uint32 periodCount;
3477 } ma_device_descriptor;
3480 These are the callbacks required to be implemented for a backend. These callbacks are grouped into two parts: context and device. There is one context
3481 to many devices. A device is created from a context.
3483 The general flow goes like this:
3485 1) A context is created with `onContextInit()`
3486 1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required.
3487 1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required.
3488 2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was
3489 selected from device enumeration via `onContextEnumerateDevices()`.
3490 3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()`
3491 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
3492 to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by
3493 miniaudio internally.
3495 Initialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the
3496 callbacks defined in this structure.
3498 Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which
3499 physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the
3500 given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration
3501 needs to stop and the `onContextEnumerateDevices()` function return with a success code.
3503 Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID,
3504 and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the
3505 case when the device ID is NULL, in which case information about the default device needs to be retrieved.
3507 Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created.
3508 This is a little bit more complicated than initialization of the context due to it's more complicated configuration. When initializing a
3509 device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input,
3510 the data format is set to what the application wants. On output it's set to the native format which should match as closely as possible to
3511 the requested format. The conversion between the format requested by the application and the device's native format will be handled
3512 internally by miniaudio.
3514 On input, if the sample format is set to `ma_format_unknown`, the backend is free to use whatever sample format it desires, so long as it's
3515 supported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for
3516 sample rate. For the channel map, the default should be used when `ma_channel_map_blank()` returns true (all channels set to
3517 `MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should
3518 inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period
3519 size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the
3520 sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_data_format`
3521 object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set).
3523 Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses
3524 asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should always be implemented.
3526 The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit
3527 easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and
3528 `onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the
3529 backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within it's callback.
3530 This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback.
3532 If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceWorkerThread()` callback
3533 which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional.
3535 The audio thread should run data delivery logic in a loop while `ma_device_get_state() == MA_STATE_STARTED` and no errors have been
3536 encounted. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback.
3538 The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this
3539 callback. When the device is stopped, the `ma_device_get_state() == MA_STATE_STARTED` condition will fail and the loop will be terminated
3540 which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback,
3541 look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to
3542 wake up the audio thread.
3544 struct ma_backend_callbacks
3546 ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks);
3547 ma_result (* onContextUninit)(ma_context* pContext);
3548 ma_result (* onContextEnumerateDevices)(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData);
3549 ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo);
3550 ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture);
3551 ma_result (* onDeviceUninit)(ma_device* pDevice);
3552 ma_result (* onDeviceStart)(ma_device* pDevice);
3553 ma_result (* onDeviceStop)(ma_device* pDevice);
3554 ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead);
3555 ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten);
3556 ma_result (* onDeviceDataLoop)(ma_device* pDevice);
3557 ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice);
3560 struct ma_context_config
3562 ma_log_proc logCallback;
3563 ma_thread_priority threadPriority;
3564 size_t threadStackSize;
3566 ma_allocation_callbacks allocationCallbacks;
3569 ma_bool32 useVerboseDeviceEnumeration;
3573 const char* pApplicationName;
3574 const char* pServerName;
3575 ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */
3579 ma_ios_session_category sessionCategory;
3580 ma_uint32 sessionCategoryOptions;
3581 ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */
3582 ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */
3586 const char* pClientName;
3587 ma_bool32 tryStartServer;
3589 ma_backend_callbacks custom;
3592 /* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */
3596 ma_event* pEvent; /* This will be signalled when the event is complete. */
3605 ma_device_type deviceType;
3607 void** ppAudioClientService;
3608 ma_result result; /* The result from creating the audio client service. */
3609 } createAudioClient;
3613 ma_device_type deviceType;
3614 } releaseAudioClient;
3616 } ma_context_command__wasapi;
3620 ma_backend_callbacks callbacks;
3621 ma_backend backend; /* DirectSound, ALSA, etc. */
3622 ma_log_proc logCallback;
3623 ma_thread_priority threadPriority;
3624 size_t threadStackSize;
3626 ma_allocation_callbacks allocationCallbacks;
3627 ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */
3628 ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */
3629 ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */
3630 ma_uint32 playbackDeviceInfoCount;
3631 ma_uint32 captureDeviceInfoCount;
3632 ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */
3636 #ifdef MA_SUPPORT_WASAPI
3639 ma_thread commandThread;
3640 ma_mutex commandLock;
3641 ma_semaphore commandSem;
3642 ma_uint32 commandIndex;
3643 ma_uint32 commandCount;
3644 ma_context_command__wasapi commands[4];
3647 #ifdef MA_SUPPORT_DSOUND
3650 ma_handle hDSoundDLL;
3651 ma_proc DirectSoundCreate;
3652 ma_proc DirectSoundEnumerateA;
3653 ma_proc DirectSoundCaptureCreate;
3654 ma_proc DirectSoundCaptureEnumerateA;
3657 #ifdef MA_SUPPORT_WINMM
3661 ma_proc waveOutGetNumDevs;
3662 ma_proc waveOutGetDevCapsA;
3663 ma_proc waveOutOpen;
3664 ma_proc waveOutClose;
3665 ma_proc waveOutPrepareHeader;
3666 ma_proc waveOutUnprepareHeader;
3667 ma_proc waveOutWrite;
3668 ma_proc waveOutReset;
3669 ma_proc waveInGetNumDevs;
3670 ma_proc waveInGetDevCapsA;
3672 ma_proc waveInClose;
3673 ma_proc waveInPrepareHeader;
3674 ma_proc waveInUnprepareHeader;
3675 ma_proc waveInAddBuffer;
3676 ma_proc waveInStart;
3677 ma_proc waveInReset;
3680 #ifdef MA_SUPPORT_ALSA
3684 ma_proc snd_pcm_open;
3685 ma_proc snd_pcm_close;
3686 ma_proc snd_pcm_hw_params_sizeof;
3687 ma_proc snd_pcm_hw_params_any;
3688 ma_proc snd_pcm_hw_params_set_format;
3689 ma_proc snd_pcm_hw_params_set_format_first;
3690 ma_proc snd_pcm_hw_params_get_format_mask;
3691 ma_proc snd_pcm_hw_params_set_channels;
3692 ma_proc snd_pcm_hw_params_set_channels_near;
3693 ma_proc snd_pcm_hw_params_set_channels_minmax;
3694 ma_proc snd_pcm_hw_params_set_rate_resample;
3695 ma_proc snd_pcm_hw_params_set_rate;
3696 ma_proc snd_pcm_hw_params_set_rate_near;
3697 ma_proc snd_pcm_hw_params_set_buffer_size_near;
3698 ma_proc snd_pcm_hw_params_set_periods_near;
3699 ma_proc snd_pcm_hw_params_set_access;
3700 ma_proc snd_pcm_hw_params_get_format;
3701 ma_proc snd_pcm_hw_params_get_channels;
3702 ma_proc snd_pcm_hw_params_get_channels_min;
3703 ma_proc snd_pcm_hw_params_get_channels_max;
3704 ma_proc snd_pcm_hw_params_get_rate;
3705 ma_proc snd_pcm_hw_params_get_rate_min;
3706 ma_proc snd_pcm_hw_params_get_rate_max;
3707 ma_proc snd_pcm_hw_params_get_buffer_size;
3708 ma_proc snd_pcm_hw_params_get_periods;
3709 ma_proc snd_pcm_hw_params_get_access;
3710 ma_proc snd_pcm_hw_params_test_format;
3711 ma_proc snd_pcm_hw_params_test_channels;
3712 ma_proc snd_pcm_hw_params_test_rate;
3713 ma_proc snd_pcm_hw_params;
3714 ma_proc snd_pcm_sw_params_sizeof;
3715 ma_proc snd_pcm_sw_params_current;
3716 ma_proc snd_pcm_sw_params_get_boundary;
3717 ma_proc snd_pcm_sw_params_set_avail_min;
3718 ma_proc snd_pcm_sw_params_set_start_threshold;
3719 ma_proc snd_pcm_sw_params_set_stop_threshold;
3720 ma_proc snd_pcm_sw_params;
3721 ma_proc snd_pcm_format_mask_sizeof;
3722 ma_proc snd_pcm_format_mask_test;
3723 ma_proc snd_pcm_get_chmap;
3724 ma_proc snd_pcm_state;
3725 ma_proc snd_pcm_prepare;
3726 ma_proc snd_pcm_start;
3727 ma_proc snd_pcm_drop;
3728 ma_proc snd_pcm_drain;
3729 ma_proc snd_device_name_hint;
3730 ma_proc snd_device_name_get_hint;
3731 ma_proc snd_card_get_index;
3732 ma_proc snd_device_name_free_hint;
3733 ma_proc snd_pcm_mmap_begin;
3734 ma_proc snd_pcm_mmap_commit;
3735 ma_proc snd_pcm_recover;
3736 ma_proc snd_pcm_readi;
3737 ma_proc snd_pcm_writei;
3738 ma_proc snd_pcm_avail;
3739 ma_proc snd_pcm_avail_update;
3740 ma_proc snd_pcm_wait;
3741 ma_proc snd_pcm_info;
3742 ma_proc snd_pcm_info_sizeof;
3743 ma_proc snd_pcm_info_get_name;
3744 ma_proc snd_config_update_free_global;
3746 ma_mutex internalDeviceEnumLock;
3747 ma_bool32 useVerboseDeviceEnumeration;
3750 #ifdef MA_SUPPORT_PULSEAUDIO
3754 ma_proc pa_mainloop_new;
3755 ma_proc pa_mainloop_free;
3756 ma_proc pa_mainloop_quit;
3757 ma_proc pa_mainloop_get_api;
3758 ma_proc pa_mainloop_iterate;
3759 ma_proc pa_mainloop_wakeup;
3760 ma_proc pa_threaded_mainloop_new;
3761 ma_proc pa_threaded_mainloop_free;
3762 ma_proc pa_threaded_mainloop_start;
3763 ma_proc pa_threaded_mainloop_stop;
3764 ma_proc pa_threaded_mainloop_lock;
3765 ma_proc pa_threaded_mainloop_unlock;
3766 ma_proc pa_threaded_mainloop_wait;
3767 ma_proc pa_threaded_mainloop_signal;
3768 ma_proc pa_threaded_mainloop_accept;
3769 ma_proc pa_threaded_mainloop_get_retval;
3770 ma_proc pa_threaded_mainloop_get_api;
3771 ma_proc pa_threaded_mainloop_in_thread;
3772 ma_proc pa_threaded_mainloop_set_name;
3773 ma_proc pa_context_new;
3774 ma_proc pa_context_unref;
3775 ma_proc pa_context_connect;
3776 ma_proc pa_context_disconnect;
3777 ma_proc pa_context_set_state_callback;
3778 ma_proc pa_context_get_state;
3779 ma_proc pa_context_get_sink_info_list;
3780 ma_proc pa_context_get_source_info_list;
3781 ma_proc pa_context_get_sink_info_by_name;
3782 ma_proc pa_context_get_source_info_by_name;
3783 ma_proc pa_operation_unref;
3784 ma_proc pa_operation_get_state;
3785 ma_proc pa_channel_map_init_extend;
3786 ma_proc pa_channel_map_valid;
3787 ma_proc pa_channel_map_compatible;
3788 ma_proc pa_stream_new;
3789 ma_proc pa_stream_unref;
3790 ma_proc pa_stream_connect_playback;
3791 ma_proc pa_stream_connect_record;
3792 ma_proc pa_stream_disconnect;
3793 ma_proc pa_stream_get_state;
3794 ma_proc pa_stream_get_sample_spec;
3795 ma_proc pa_stream_get_channel_map;
3796 ma_proc pa_stream_get_buffer_attr;
3797 ma_proc pa_stream_set_buffer_attr;
3798 ma_proc pa_stream_get_device_name;
3799 ma_proc pa_stream_set_write_callback;
3800 ma_proc pa_stream_set_read_callback;
3801 ma_proc pa_stream_flush;
3802 ma_proc pa_stream_drain;
3803 ma_proc pa_stream_is_corked;
3804 ma_proc pa_stream_cork;
3805 ma_proc pa_stream_trigger;
3806 ma_proc pa_stream_begin_write;
3807 ma_proc pa_stream_write;
3808 ma_proc pa_stream_peek;
3809 ma_proc pa_stream_drop;
3810 ma_proc pa_stream_writable_size;
3811 ma_proc pa_stream_readable_size;
3813 /*pa_mainloop**/ ma_ptr pMainLoop;
3814 /*pa_context**/ ma_ptr pPulseContext;
3817 #ifdef MA_SUPPORT_JACK
3821 ma_proc jack_client_open;
3822 ma_proc jack_client_close;
3823 ma_proc jack_client_name_size;
3824 ma_proc jack_set_process_callback;
3825 ma_proc jack_set_buffer_size_callback;
3826 ma_proc jack_on_shutdown;
3827 ma_proc jack_get_sample_rate;
3828 ma_proc jack_get_buffer_size;
3829 ma_proc jack_get_ports;
3830 ma_proc jack_activate;
3831 ma_proc jack_deactivate;
3832 ma_proc jack_connect;
3833 ma_proc jack_port_register;
3834 ma_proc jack_port_name;
3835 ma_proc jack_port_get_buffer;
3839 ma_bool32 tryStartServer;
3842 #ifdef MA_SUPPORT_COREAUDIO
3845 ma_handle hCoreFoundation;
3846 ma_proc CFStringGetCString;
3849 ma_handle hCoreAudio;
3850 ma_proc AudioObjectGetPropertyData;
3851 ma_proc AudioObjectGetPropertyDataSize;
3852 ma_proc AudioObjectSetPropertyData;
3853 ma_proc AudioObjectAddPropertyListener;
3854 ma_proc AudioObjectRemovePropertyListener;
3856 ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */
3857 ma_proc AudioComponentFindNext;
3858 ma_proc AudioComponentInstanceDispose;
3859 ma_proc AudioComponentInstanceNew;
3860 ma_proc AudioOutputUnitStart;
3861 ma_proc AudioOutputUnitStop;
3862 ma_proc AudioUnitAddPropertyListener;
3863 ma_proc AudioUnitGetPropertyInfo;
3864 ma_proc AudioUnitGetProperty;
3865 ma_proc AudioUnitSetProperty;
3866 ma_proc AudioUnitInitialize;
3867 ma_proc AudioUnitRender;
3869 /*AudioComponent*/ ma_ptr component;
3870 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(). */
3873 #ifdef MA_SUPPORT_SNDIO
3889 ma_proc sio_revents;
3893 ma_proc sio_initpar;
3896 #ifdef MA_SUPPORT_AUDIO4
3902 #ifdef MA_SUPPORT_OSS
3909 #ifdef MA_SUPPORT_AAUDIO
3912 ma_handle hAAudio; /* libaaudio.so */
3913 ma_proc AAudio_createStreamBuilder;
3914 ma_proc AAudioStreamBuilder_delete;
3915 ma_proc AAudioStreamBuilder_setDeviceId;
3916 ma_proc AAudioStreamBuilder_setDirection;
3917 ma_proc AAudioStreamBuilder_setSharingMode;
3918 ma_proc AAudioStreamBuilder_setFormat;
3919 ma_proc AAudioStreamBuilder_setChannelCount;
3920 ma_proc AAudioStreamBuilder_setSampleRate;
3921 ma_proc AAudioStreamBuilder_setBufferCapacityInFrames;
3922 ma_proc AAudioStreamBuilder_setFramesPerDataCallback;
3923 ma_proc AAudioStreamBuilder_setDataCallback;
3924 ma_proc AAudioStreamBuilder_setErrorCallback;
3925 ma_proc AAudioStreamBuilder_setPerformanceMode;
3926 ma_proc AAudioStreamBuilder_setUsage;
3927 ma_proc AAudioStreamBuilder_setContentType;
3928 ma_proc AAudioStreamBuilder_setInputPreset;
3929 ma_proc AAudioStreamBuilder_openStream;
3930 ma_proc AAudioStream_close;
3931 ma_proc AAudioStream_getState;
3932 ma_proc AAudioStream_waitForStateChange;
3933 ma_proc AAudioStream_getFormat;
3934 ma_proc AAudioStream_getChannelCount;
3935 ma_proc AAudioStream_getSampleRate;
3936 ma_proc AAudioStream_getBufferCapacityInFrames;
3937 ma_proc AAudioStream_getFramesPerDataCallback;
3938 ma_proc AAudioStream_getFramesPerBurst;
3939 ma_proc AAudioStream_requestStart;
3940 ma_proc AAudioStream_requestStop;
3943 #ifdef MA_SUPPORT_OPENSL
3946 ma_handle libOpenSLES;
3947 ma_handle SL_IID_ENGINE;
3948 ma_handle SL_IID_AUDIOIODEVICECAPABILITIES;
3949 ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
3950 ma_handle SL_IID_RECORD;
3951 ma_handle SL_IID_PLAY;
3952 ma_handle SL_IID_OUTPUTMIX;
3953 ma_handle SL_IID_ANDROIDCONFIGURATION;
3954 ma_proc slCreateEngine;
3957 #ifdef MA_SUPPORT_WEBAUDIO
3963 #ifdef MA_SUPPORT_NULL
3976 /*HMODULE*/ ma_handle hOle32DLL;
3977 ma_proc CoInitializeEx;
3978 ma_proc CoUninitialize;
3979 ma_proc CoCreateInstance;
3980 ma_proc CoTaskMemFree;
3981 ma_proc PropVariantClear;
3982 ma_proc StringFromGUID2;
3984 /*HMODULE*/ ma_handle hUser32DLL;
3985 ma_proc GetForegroundWindow;
3986 ma_proc GetDesktopWindow;
3988 /*HMODULE*/ ma_handle hAdvapi32DLL;
3989 ma_proc RegOpenKeyExA;
3990 ma_proc RegCloseKey;
3991 ma_proc RegQueryValueExA;
3997 ma_handle pthreadSO;
3998 ma_proc pthread_create;
3999 ma_proc pthread_join;
4000 ma_proc pthread_mutex_init;
4001 ma_proc pthread_mutex_destroy;
4002 ma_proc pthread_mutex_lock;
4003 ma_proc pthread_mutex_unlock;
4004 ma_proc pthread_cond_init;
4005 ma_proc pthread_cond_destroy;
4006 ma_proc pthread_cond_wait;
4007 ma_proc pthread_cond_signal;
4008 ma_proc pthread_attr_init;
4009 ma_proc pthread_attr_destroy;
4010 ma_proc pthread_attr_setschedpolicy;
4011 ma_proc pthread_attr_getschedparam;
4012 ma_proc pthread_attr_setschedparam;
4021 ma_context* pContext;
4022 ma_device_type type;
4023 ma_uint32 sampleRate;
4024 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. */
4025 ma_device_callback_proc onData; /* Set once at initialization time and should not be changed after. */
4026 ma_stop_proc onStop; /* Set once at initialization time and should not be changed after. */
4027 void* pUserData; /* Application defined data. */
4028 ma_mutex startStopLock;
4029 ma_event wakeupEvent;
4030 ma_event startEvent;
4033 ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */
4034 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(). */
4035 ma_bool8 noPreZeroedOutputBuffer;
4037 MA_ATOMIC float masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */
4038 ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */
4041 ma_resample_algorithm algorithm;
4053 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. */
4054 char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */
4055 ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
4058 ma_channel channelMap[MA_MAX_CHANNELS];
4059 ma_format internalFormat;
4060 ma_uint32 internalChannels;
4061 ma_uint32 internalSampleRate;
4062 ma_channel internalChannelMap[MA_MAX_CHANNELS];
4063 ma_uint32 internalPeriodSizeInFrames;
4064 ma_uint32 internalPeriods;
4065 ma_channel_mix_mode channelMixMode;
4066 ma_data_converter converter;
4070 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. */
4071 char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */
4072 ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
4075 ma_channel channelMap[MA_MAX_CHANNELS];
4076 ma_format internalFormat;
4077 ma_uint32 internalChannels;
4078 ma_uint32 internalSampleRate;
4079 ma_channel internalChannelMap[MA_MAX_CHANNELS];
4080 ma_uint32 internalPeriodSizeInFrames;
4081 ma_uint32 internalPeriods;
4082 ma_channel_mix_mode channelMixMode;
4083 ma_data_converter converter;
4088 #ifdef MA_SUPPORT_WASAPI
4091 /*IAudioClient**/ ma_ptr pAudioClientPlayback;
4092 /*IAudioClient**/ ma_ptr pAudioClientCapture;
4093 /*IAudioRenderClient**/ ma_ptr pRenderClient;
4094 /*IAudioCaptureClient**/ ma_ptr pCaptureClient;
4095 /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */
4096 ma_IMMNotificationClient notificationClient;
4097 /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */
4098 /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */
4099 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. */
4100 ma_uint32 actualPeriodSizeInFramesCapture;
4101 ma_uint32 originalPeriodSizeInFrames;
4102 ma_uint32 originalPeriodSizeInMilliseconds;
4103 ma_uint32 originalPeriods;
4104 ma_performance_profile originalPerformanceProfile;
4105 ma_uint32 periodSizeInFramesPlayback;
4106 ma_uint32 periodSizeInFramesCapture;
4107 MA_ATOMIC ma_bool32 isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
4108 MA_ATOMIC ma_bool32 isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
4109 ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
4110 ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
4111 ma_bool8 noHardwareOffloading;
4112 ma_bool8 allowCaptureAutoStreamRouting;
4113 ma_bool8 allowPlaybackAutoStreamRouting;
4114 ma_bool8 isDetachedPlayback;
4115 ma_bool8 isDetachedCapture;
4118 #ifdef MA_SUPPORT_DSOUND
4121 /*LPDIRECTSOUND*/ ma_ptr pPlayback;
4122 /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer;
4123 /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer;
4124 /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture;
4125 /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer;
4128 #ifdef MA_SUPPORT_WINMM
4131 /*HWAVEOUT*/ ma_handle hDevicePlayback;
4132 /*HWAVEIN*/ ma_handle hDeviceCapture;
4133 /*HANDLE*/ ma_handle hEventPlayback;
4134 /*HANDLE*/ ma_handle hEventCapture;
4135 ma_uint32 fragmentSizeInFrames;
4136 ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */
4137 ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */
4138 ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */
4139 ma_uint32 headerFramesConsumedCapture; /* ^^^ */
4140 /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */
4141 /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */
4142 ma_uint8* pIntermediaryBufferPlayback;
4143 ma_uint8* pIntermediaryBufferCapture;
4144 ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */
4147 #ifdef MA_SUPPORT_ALSA
4150 /*snd_pcm_t**/ ma_ptr pPCMPlayback;
4151 /*snd_pcm_t**/ ma_ptr pPCMCapture;
4152 ma_bool8 isUsingMMapPlayback;
4153 ma_bool8 isUsingMMapCapture;
4156 #ifdef MA_SUPPORT_PULSEAUDIO
4159 /*pa_stream**/ ma_ptr pStreamPlayback;
4160 /*pa_stream**/ ma_ptr pStreamCapture;
4163 #ifdef MA_SUPPORT_JACK
4166 /*jack_client_t**/ ma_ptr pClient;
4167 /*jack_port_t**/ ma_ptr pPortsPlayback[MA_MAX_CHANNELS];
4168 /*jack_port_t**/ ma_ptr pPortsCapture[MA_MAX_CHANNELS];
4169 float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */
4170 float* pIntermediaryBufferCapture;
4173 #ifdef MA_SUPPORT_COREAUDIO
4176 ma_uint32 deviceObjectIDPlayback;
4177 ma_uint32 deviceObjectIDCapture;
4178 /*AudioUnit*/ ma_ptr audioUnitPlayback;
4179 /*AudioUnit*/ ma_ptr audioUnitCapture;
4180 /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */
4181 ma_uint32 audioBufferCapInFrames; /* Only used for input devices. The capacity in frames of each buffer in pAudioBufferList. */
4183 ma_uint32 originalPeriodSizeInFrames;
4184 ma_uint32 originalPeriodSizeInMilliseconds;
4185 ma_uint32 originalPeriods;
4186 ma_performance_profile originalPerformanceProfile;
4187 ma_bool32 isDefaultPlaybackDevice;
4188 ma_bool32 isDefaultCaptureDevice;
4189 ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
4190 ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
4191 void* pRouteChangeHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */
4194 #ifdef MA_SUPPORT_SNDIO
4197 ma_ptr handlePlayback;
4198 ma_ptr handleCapture;
4199 ma_bool32 isStartedPlayback;
4200 ma_bool32 isStartedCapture;
4203 #ifdef MA_SUPPORT_AUDIO4
4210 #ifdef MA_SUPPORT_OSS
4217 #ifdef MA_SUPPORT_AAUDIO
4220 /*AAudioStream**/ ma_ptr pStreamPlayback;
4221 /*AAudioStream**/ ma_ptr pStreamCapture;
4224 #ifdef MA_SUPPORT_OPENSL
4227 /*SLObjectItf*/ ma_ptr pOutputMixObj;
4228 /*SLOutputMixItf*/ ma_ptr pOutputMix;
4229 /*SLObjectItf*/ ma_ptr pAudioPlayerObj;
4230 /*SLPlayItf*/ ma_ptr pAudioPlayer;
4231 /*SLObjectItf*/ ma_ptr pAudioRecorderObj;
4232 /*SLRecordItf*/ ma_ptr pAudioRecorder;
4233 /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback;
4234 /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture;
4235 ma_bool32 isDrainingCapture;
4236 ma_bool32 isDrainingPlayback;
4237 ma_uint32 currentBufferIndexPlayback;
4238 ma_uint32 currentBufferIndexCapture;
4239 ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */
4240 ma_uint8* pBufferCapture;
4243 #ifdef MA_SUPPORT_WEBAUDIO
4246 int indexPlayback; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */
4250 #ifdef MA_SUPPORT_NULL
4253 ma_thread deviceThread;
4254 ma_event operationEvent;
4255 ma_event operationCompletionEvent;
4256 ma_semaphore operationSemaphore;
4257 ma_uint32 operation;
4258 ma_result operationResult;
4260 double priorRunTime;
4261 ma_uint32 currentPeriodFramesRemainingPlayback;
4262 ma_uint32 currentPeriodFramesRemainingCapture;
4263 ma_uint64 lastProcessedFramePlayback;
4264 ma_uint64 lastProcessedFrameCapture;
4265 MA_ATOMIC ma_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */
4270 #if defined(_MSC_VER) && !defined(__clang__)
4271 #pragma warning(pop)
4272 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
4273 #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
4277 Initializes a `ma_context_config` object.
4282 A `ma_context_config` initialized to defaults.
4287 You must always use this to initialize the default state of the `ma_context_config` object. Not using this will result in your program breaking when miniaudio
4288 is updated and new members are added to `ma_context_config`. It also sets logical defaults.
4290 You can override members of the returned object by changing it's members directly.
4297 MA_API ma_context_config ma_context_config_init(void);
4300 Initializes a context.
4302 The context is used for selecting and initializing an appropriate backend and to represent the backend at a more global level than that of an individual
4303 device. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices.
4308 backends (in, optional)
4309 A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order.
4311 backendCount (in, optional)
4312 The number of items in `backend`. Ignored if `backend` is NULL.
4314 pConfig (in, optional)
4315 The context configuration.
4318 A pointer to the context object being initialized.
4323 MA_SUCCESS if successful; any other error code otherwise.
4328 Unsafe. Do not call this function across multiple threads as some backends read and write to global state.
4333 When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order:
4335 |-------------|-----------------------|--------------------------------------------------------|
4336 | Name | Enum Name | Supported Operating Systems |
4337 |-------------|-----------------------|--------------------------------------------------------|
4338 | WASAPI | ma_backend_wasapi | Windows Vista+ |
4339 | DirectSound | ma_backend_dsound | Windows XP+ |
4340 | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) |
4341 | Core Audio | ma_backend_coreaudio | macOS, iOS |
4342 | ALSA | ma_backend_alsa | Linux |
4343 | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
4344 | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
4345 | sndio | ma_backend_sndio | OpenBSD |
4346 | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
4347 | OSS | ma_backend_oss | FreeBSD |
4348 | AAudio | ma_backend_aaudio | Android 8+ |
4349 | OpenSL|ES | ma_backend_opensl | Android (API level 16+) |
4350 | Web Audio | ma_backend_webaudio | Web (via Emscripten) |
4351 | Null | ma_backend_null | Cross Platform (not used on Web) |
4352 |-------------|-----------------------|--------------------------------------------------------|
4354 The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings
4355 can then be set directly on the structure. Below are the members of the `ma_context_config` object.
4358 Callback for handling log messages from miniaudio.
4361 The desired priority to use for the audio thread. Allowable values include the following:
4363 |--------------------------------------|
4365 |--------------------------------------|
4366 | ma_thread_priority_idle |
4367 | ma_thread_priority_lowest |
4368 | ma_thread_priority_low |
4369 | ma_thread_priority_normal |
4370 | ma_thread_priority_high |
4371 | ma_thread_priority_highest (default) |
4372 | ma_thread_priority_realtime |
4373 | ma_thread_priority_default |
4374 |--------------------------------------|
4377 A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`.
4380 Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation
4381 callbacks will be used for anything tied to the context, including devices.
4383 alsa.useVerboseDeviceEnumeration
4384 ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique
4385 card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes
4386 it so the ALSA backend includes all devices. Defaults to false.
4388 pulse.pApplicationName
4389 PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`.
4392 PulseAudio only. The name of the server to connect to with `pa_context_connect()`.
4395 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
4396 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
4397 intrusive for the end user.
4399 coreaudio.sessionCategory
4400 iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.
4402 |-----------------------------------------|-------------------------------------|
4403 | miniaudio Token | Core Audio Token |
4404 |-----------------------------------------|-------------------------------------|
4405 | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient |
4406 | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient |
4407 | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback |
4408 | ma_ios_session_category_record | AVAudioSessionCategoryRecord |
4409 | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord |
4410 | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute |
4411 | ma_ios_session_category_none | AVAudioSessionCategoryAmbient |
4412 | ma_ios_session_category_default | AVAudioSessionCategoryAmbient |
4413 |-----------------------------------------|-------------------------------------|
4415 coreaudio.sessionCategoryOptions
4416 iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.
4418 |---------------------------------------------------------------------------|------------------------------------------------------------------|
4419 | miniaudio Token | Core Audio Token |
4420 |---------------------------------------------------------------------------|------------------------------------------------------------------|
4421 | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers |
4422 | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers |
4423 | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth |
4424 | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker |
4425 | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers |
4426 | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP |
4427 | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay |
4428 |---------------------------------------------------------------------------|------------------------------------------------------------------|
4431 The name of the client to pass to `jack_client_open()`.
4434 Whether or not to try auto-starting the JACK server. Defaults to false.
4437 It is recommended that only a single context is active at any given time because it's a bulky data structure which performs run-time linking for the
4438 relevant backends every time it's initialized.
4440 The location of the context cannot change throughout it's lifetime. Consider allocating the `ma_context` object with `malloc()` if this is an issue. The
4441 reason for this is that a pointer to the context is stored in the `ma_device` structure.
4444 Example 1 - Default Initialization
4445 ----------------------------------
4446 The example below shows how to initialize the context using the default configuration.
4450 ma_result result = ma_context_init(NULL, 0, NULL, &context);
4451 if (result != MA_SUCCESS) {
4457 Example 2 - Custom Configuration
4458 --------------------------------
4459 The example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program
4460 wants to prioritize ALSA over PulseAudio on Linux. They also want to avoid using the WinMM backend on Windows because it's latency is too high. They also
4461 want an error to be returned if no valid backend is available which they achieve by excluding the Null backend.
4463 For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface.
4466 ma_backend backends[] = {
4468 ma_backend_pulseaudio,
4473 ma_context_config config = ma_context_config_init();
4474 config.logCallback = my_log_callback;
4475 config.pUserData = pMyUserData;
4478 ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context);
4479 if (result != MA_SUCCESS) {
4481 if (result == MA_NO_BACKEND) {
4482 // Couldn't find an appropriate backend.
4490 ma_context_config_init()
4493 MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext);
4496 Uninitializes a context.
4501 MA_SUCCESS if successful; any other error code otherwise.
4506 Unsafe. Do not call this function across multiple threads as some backends read and write to global state.
4511 Results are undefined if you call this while any device created by this context is still active.
4518 MA_API ma_result ma_context_uninit(ma_context* pContext);
4521 Retrieves the size of the ma_context object.
4523 This is mainly for the purpose of bindings to know how much memory to allocate.
4525 MA_API size_t ma_context_sizeof(void);
4528 Enumerates over every device (both playback and capture).
4530 This is a lower-level enumeration function to the easier to use `ma_context_get_devices()`. Use `ma_context_enumerate_devices()` if you would rather not incur
4531 an internal heap allocation, or it simply suits your code better.
4533 Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require
4534 opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this,
4535 but don't call it from within the enumeration callback.
4537 Returning false from the callback will stop enumeration. Returning true will continue enumeration.
4543 A pointer to the context performing the enumeration.
4546 The callback to fire for each enumerated device.
4549 A pointer to application-defined data passed to the callback.
4554 MA_SUCCESS if successful; any other error code otherwise.
4559 Safe. This is guarded using a simple mutex lock.
4564 Do _not_ assume the first enumerated device of a given type is the default device.
4566 Some backends and platforms may only support default playback and capture devices.
4568 In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also,
4569 do not try to call `ma_context_get_device_info()` from within the callback.
4571 Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation.
4574 Example 1 - Simple Enumeration
4575 ------------------------------
4576 ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
4578 printf("Device Name: %s\n", pInfo->name);
4582 ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData);
4583 if (result != MA_SUCCESS) {
4590 ma_context_get_devices()
4592 MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData);
4595 Retrieves basic information about every active playback and/or capture device.
4597 This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos`
4598 parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback.
4604 A pointer to the context performing the enumeration.
4606 ppPlaybackDeviceInfos (out)
4607 A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices.
4609 pPlaybackDeviceCount (out)
4610 A pointer to an unsigned integer that will receive the number of playback devices.
4612 ppCaptureDeviceInfos (out)
4613 A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices.
4615 pCaptureDeviceCount (out)
4616 A pointer to an unsigned integer that will receive the number of capture devices.
4621 MA_SUCCESS if successful; any other error code otherwise.
4626 Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple
4627 threads. Instead, you need to make a copy of the returned data with your own higher level synchronization.
4632 It is _not_ safe to assume the first device in the list is the default device.
4634 You can pass in NULL for the playback or capture lists in which case they'll be ignored.
4636 The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers.
4641 ma_context_get_devices()
4643 MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount);
4646 Retrieves information about a device of the given type, with the specified ID and share mode.
4652 A pointer to the context performing the query.
4655 The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`.
4658 The ID of the device being queried.
4661 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,
4662 set this to `ma_share_mode_shared`.
4665 A pointer to the `ma_device_info` structure that will receive the device information.
4670 MA_SUCCESS if successful; any other error code otherwise.
4675 Safe. This is guarded using a simple mutex lock.
4680 Do _not_ call this from within the `ma_context_enumerate_devices()` callback.
4682 It's possible for a device to have different information and capabilities depending on whether or not it's opened in shared or exclusive mode. For example, in
4683 shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify
4684 which share mode you want information for. Note that not all backends and devices support shared or exclusive mode, in which case this function will fail if
4685 the requested share mode is unsupported.
4687 This leaves pDeviceInfo unmodified in the result of an error.
4689 MA_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);
4692 Determines if the given context supports loopback mode.
4698 A pointer to the context getting queried.
4703 MA_TRUE if the context supports loopback mode; MA_FALSE otherwise.
4705 MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext);
4710 Initializes a device config with default settings.
4716 The type of the device this config is being initialized for. This must set to one of the following:
4718 |-------------------------|
4720 |-------------------------|
4721 | ma_device_type_playback |
4722 | ma_device_type_capture |
4723 | ma_device_type_duplex |
4724 | ma_device_type_loopback |
4725 |-------------------------|
4730 A new device config object with default settings. You will typically want to adjust the config after this function returns. See remarks.
4740 Safe, but don't try initializing a device in a callback.
4745 The returned config will be initialized to defaults. You will normally want to customize a few variables before initializing the device. See Example 1 for a
4746 typical configuration which sets the sample format, channel count, sample rate, data callback and user data. These are usually things you will want to change
4747 before initializing the device.
4749 See `ma_device_init()` for details on specific configuration options.
4752 Example 1 - Simple Configuration
4753 --------------------------------
4754 The example below is what a program will typically want to configure for each device at a minimum. Notice how `ma_device_config_init()` is called first, and
4755 then the returned object is modified directly. This is important because it ensures that your program continues to work as new configuration options are added
4756 to the `ma_device_config` structure.
4759 ma_device_config config = ma_device_config_init(ma_device_type_playback);
4760 config.playback.format = ma_format_f32;
4761 config.playback.channels = 2;
4762 config.sampleRate = 48000;
4763 config.dataCallback = ma_data_callback;
4764 config.pUserData = pMyUserData;
4773 MA_API ma_device_config ma_device_config_init(ma_device_type deviceType);
4777 Initializes a device.
4779 A device represents a physical audio device. The idea is you send or receive audio data from the device to either play it back through a speaker, or capture it
4780 from a microphone. Whether or not you should send or receive data from the device (or both) depends on the type of device you are initializing which can be
4781 playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the
4782 device is done via a callback which is fired by miniaudio at periodic time intervals.
4784 The frequency at which data is delivered to and from a device depends on the size of it's period. The size of the period can be defined in terms of PCM frames
4785 or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and
4786 increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but
4787 miniaudio's defaults should work fine for most scenarios. If you're building a game you should leave this fairly small, whereas if you're building a simple
4788 media player you can make it larger. Note that the period size you request is actually just a hint - miniaudio will tell the backend what you want, but the
4789 backend is ultimately responsible for what it gives you. You cannot assume you will get exactly what you ask for.
4791 When delivering data to and from a device you need to make sure it's in the correct format which you can set through the device configuration. You just set the
4792 format that you want to use and miniaudio will perform all of the necessary conversion for you internally. When delivering data to and from the callback you
4793 can assume the format is the same as what you requested when you initialized the device. See Remarks for more details on miniaudio's data conversion pipeline.
4798 pContext (in, optional)
4799 A pointer to the context that owns the device. This can be null, in which case it creates a default context internally.
4802 A pointer to the device configuration. Cannot be null. See remarks for details.
4805 A pointer to the device object being initialized.
4810 MA_SUCCESS if successful; any other error code otherwise.
4815 Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to
4816 calling this at the same time as `ma_device_uninit()`.
4821 Unsafe. It is not safe to call this inside any callback.
4826 Setting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so:
4829 ma_context_init(NULL, 0, NULL, &context);
4832 Do not set `pContext` to NULL if you are needing to open multiple devices. You can, however, use NULL when initializing the first device, and then use
4833 device.pContext for the initialization of other devices.
4835 The device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can
4836 then be set directly on the structure. Below are the members of the `ma_device_config` object.
4839 Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`.
4842 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.
4845 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
4846 be used depending on the selected performance profile. This value affects latency. See below for details.
4848 periodSizeInMilliseconds
4849 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
4850 used depending on the selected performance profile. The value affects latency. See below for details.
4853 The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by
4854 this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured.
4857 A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or
4858 `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value.
4860 noPreZeroedOutputBuffer
4861 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
4862 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
4863 callback will write to every sample in the output buffer, or if you are doing your own clearing.
4866 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
4867 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
4868 applies when the playback sample format is f32.
4871 The callback to fire whenever data is ready to be delivered to or from the device.
4874 The callback to fire whenever the device has stopped, either explicitly via `ma_device_stop()`, or implicitly due to things like the device being
4878 The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`.
4880 resampling.algorithm
4881 The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The
4882 default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`.
4884 resampling.linear.lpfOrder
4885 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
4886 the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is
4887 `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`.
4890 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
4891 default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration.
4894 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
4895 initialization from the device object directly with `device.playback.format`.
4898 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
4899 from the device object directly with `device.playback.channels`.
4902 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
4903 device object direct with `device.playback.channelMap`.
4906 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
4907 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
4908 ma_share_mode_shared and reinitializing.
4911 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
4912 default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration.
4915 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
4916 initialization from the device object directly with `device.capture.format`.
4919 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
4920 from the device object directly with `device.capture.channels`.
4923 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
4924 device object direct with `device.capture.channelMap`.
4927 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
4928 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
4929 ma_share_mode_shared and reinitializing.
4931 wasapi.noAutoConvertSRC
4932 WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false.
4934 wasapi.noDefaultQualitySRC
4935 WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`.
4936 You should usually leave this set to false, which is the default.
4938 wasapi.noAutoStreamRouting
4939 WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false.
4941 wasapi.noHardwareOffloading
4942 WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false.
4945 ALSA only. When set to true, disables MMap mode. Defaults to false.
4948 ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false.
4951 ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false.
4954 ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false.
4956 pulse.pStreamNamePlayback
4957 PulseAudio only. Sets the stream name for playback.
4959 pulse.pStreamNameCapture
4960 PulseAudio only. Sets the stream name for capture.
4962 coreaudio.allowNominalSampleRateChange
4963 Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This
4964 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
4965 that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will
4966 find the closest match between the sample rate requested in the device config and the sample rates natively supported by the
4967 hardware. When set to false, the sample rate currently set by the operating system will always be used.
4970 Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device.
4972 After initializing the device it will be in a stopped state. To start it, use `ma_device_start()`.
4974 If both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or
4975 `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or
4976 `ma_performance_profile_conservative`.
4978 If you request exclusive mode and the backend does not support it an error will be returned. For robustness, you may want to first try initializing the device
4979 in exclusive mode, and then fall back to shared mode if required. Alternatively you can just request shared mode (the default if you leave it unset in the
4980 config) which is the most reliable option. Some backends do not have a practical way of choosing whether or not the device should be exclusive or not (ALSA,
4981 for example) in which case it just acts as a hint. Unless you have special requirements you should try avoiding exclusive mode as it's intrusive to the user.
4982 Starting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary.
4984 When sending or receiving data to/from a device, miniaudio will internally perform a format conversion to convert between the format specified by the config
4985 and the format used internally by the backend. If you pass in 0 for the sample format, channel count, sample rate _and_ channel map, data transmission will run
4986 on an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`,
4987 `playback/capture.channels` and `sampleRate` members of the device object.
4989 When compiling for UWP you must ensure you call this function on the main UI thread because the operating system may need to present the user with a message
4990 asking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information.
4992 ALSA Specific: When initializing the default device, requesting shared mode will try using the "dmix" device for playback and the "dsnoop" device for capture.
4993 If these fail it will try falling back to the "hw" device.
4996 Example 1 - Simple Initialization
4997 ---------------------------------
4998 This example shows how to initialize a simple playback device using a standard configuration. If you are just needing to do simple playback from the default
4999 playback device this is usually all you need.
5002 ma_device_config config = ma_device_config_init(ma_device_type_playback);
5003 config.playback.format = ma_format_f32;
5004 config.playback.channels = 2;
5005 config.sampleRate = 48000;
5006 config.dataCallback = ma_data_callback;
5007 config.pMyUserData = pMyUserData;
5010 ma_result result = ma_device_init(NULL, &config, &device);
5011 if (result != MA_SUCCESS) {
5017 Example 2 - Advanced Initialization
5018 -----------------------------------
5019 This example shows how you might do some more advanced initialization. In this hypothetical example we want to control the latency by setting the buffer size
5020 and period count. We also want to allow the user to be able to choose which device to output from which means we need a context so we can perform device
5025 ma_result result = ma_context_init(NULL, 0, NULL, &context);
5026 if (result != MA_SUCCESS) {
5030 ma_device_info* pPlaybackDeviceInfos;
5031 ma_uint32 playbackDeviceCount;
5032 result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL);
5033 if (result != MA_SUCCESS) {
5037 // ... choose a device from pPlaybackDeviceInfos ...
5039 ma_device_config config = ma_device_config_init(ma_device_type_playback);
5040 config.playback.pDeviceID = pMyChosenDeviceID; // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices().
5041 config.playback.format = ma_format_f32;
5042 config.playback.channels = 2;
5043 config.sampleRate = 48000;
5044 config.dataCallback = ma_data_callback;
5045 config.pUserData = pMyUserData;
5046 config.periodSizeInMilliseconds = 10;
5050 result = ma_device_init(&context, &config, &device);
5051 if (result != MA_SUCCESS) {
5059 ma_device_config_init()
5063 ma_context_get_devices()
5064 ma_context_enumerate_devices()
5066 MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice);
5069 Initializes a device without a context, with extra parameters for controlling the configuration of the internal self-managed context.
5071 This is the same as `ma_device_init()`, only instead of a context being passed in, the parameters from `ma_context_init()` are passed in instead. This function
5072 allows you to configure the internally created context.
5077 backends (in, optional)
5078 A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order.
5080 backendCount (in, optional)
5081 The number of items in `backend`. Ignored if `backend` is NULL.
5083 pContextConfig (in, optional)
5084 The context configuration.
5087 A pointer to the device configuration. Cannot be null. See remarks for details.
5090 A pointer to the device object being initialized.
5095 MA_SUCCESS if successful; any other error code otherwise.
5100 Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to
5101 calling this at the same time as `ma_device_uninit()`.
5106 Unsafe. It is not safe to call this inside any callback.
5111 You only need to use this function if you want to configure the context differently to it's defaults. You should never use this function if you want to manage
5114 See the documentation for `ma_context_init()` for information on the different context configuration options.
5121 ma_device_config_init()
5124 MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice);
5127 Uninitializes a device.
5129 This will explicitly stop the device. You do not need to call `ma_device_stop()` beforehand, but it's harmless if you do.
5135 A pointer to the device to stop.
5145 Unsafe. As soon as this API is called the device should be considered undefined.
5150 Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.
5158 MA_API void ma_device_uninit(ma_device* pDevice);
5161 Starts the device. For playback devices this begins playback. For capture devices it begins recording.
5163 Use `ma_device_stop()` to stop the device.
5169 A pointer to the device to start.
5174 MA_SUCCESS if successful; any other error code otherwise.
5179 Safe. It's safe to call this from any thread with the exception of the callback thread.
5184 Unsafe. It is not safe to call this inside any callback.
5189 For a playback device, this will retrieve an initial chunk of audio data from the client before returning. The reason for this is to ensure there is valid
5190 audio data in the buffer, which needs to be done before the device begins playback.
5192 This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety.
5194 Do not call this in any callback.
5201 MA_API ma_result ma_device_start(ma_device* pDevice);
5204 Stops the device. For playback devices this stops playback. For capture devices it stops recording.
5206 Use `ma_device_start()` to start the device again.
5212 A pointer to the device to stop.
5217 MA_SUCCESS if successful; any other error code otherwise.
5222 Safe. It's safe to call this from any thread with the exception of the callback thread.
5227 Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.
5232 This API needs to wait on the worker thread to stop the backend device properly before returning. It also waits on a mutex for thread-safety. In addition, some
5233 backends need to wait for the device to finish playback/recording of the current fragment which can take some time (usually proportionate to the buffer size
5234 that was specified at initialization time).
5236 Backends are required to either pause the stream in-place or drain the buffer if pausing is not possible. The reason for this is that stopping the device and
5237 the resuming it with ma_device_start() (which you might do when your program loses focus) may result in a situation where those samples are never output to the
5238 speakers or received from the microphone which can in turn result in de-syncs.
5240 Do not call this in any callback.
5242 This will be called implicitly by `ma_device_uninit()`.
5249 MA_API ma_result ma_device_stop(ma_device* pDevice);
5252 Determines whether or not the device is started.
5258 A pointer to the device whose start state is being retrieved.
5263 True if the device is started, false otherwise.
5268 Safe. If another thread calls `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, there's a very small chance the return
5269 value will be out of sync.
5274 Safe. This is implemented as a simple accessor.
5282 MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice);
5286 Retrieves the state of the device.
5292 A pointer to the device whose state is being retrieved.
5297 The current state of the device. The return value will be one of the following:
5299 +------------------------+------------------------------------------------------------------------------+
5300 | MA_STATE_UNINITIALIZED | Will only be returned if the device is in the middle of initialization. |
5301 +------------------------+------------------------------------------------------------------------------+
5302 | MA_STATE_STOPPED | The device is stopped. The initial state of the device after initialization. |
5303 +------------------------+------------------------------------------------------------------------------+
5304 | MA_STATE_STARTED | The device started and requesting and/or delivering audio data. |
5305 +------------------------+------------------------------------------------------------------------------+
5306 | MA_STATE_STARTING | The device is in the process of starting. |
5307 +------------------------+------------------------------------------------------------------------------+
5308 | MA_STATE_STOPPING | The device is in the process of stopping. |
5309 +------------------------+------------------------------------------------------------------------------+
5314 Safe. This is implemented as a simple accessor. Note that if the device is started or stopped at the same time as this function is called,
5315 there's a possibility the return value could be out of sync. See remarks.
5320 Safe. This is implemented as a simple accessor.
5325 The general flow of a devices state goes like this:
5328 ma_device_init() -> MA_STATE_UNINITIALIZED -> MA_STATE_STOPPED
5329 ma_device_start() -> MA_STATE_STARTING -> MA_STATE_STARTED
5330 ma_device_stop() -> MA_STATE_STOPPING -> MA_STATE_STOPPED
5333 When the state of the device is changed with `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, the
5334 value returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own
5337 MA_API ma_uint32 ma_device_get_state(const ma_device* pDevice);
5341 Sets the master volume factor for the device.
5343 The 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
5344 values less than 0 decreases the volume.
5350 A pointer to the device whose volume is being set.
5353 The new volume factor. Must be within the range of [0, 1].
5358 MA_SUCCESS if the volume was set successfully.
5359 MA_INVALID_ARGS if pDevice is NULL.
5360 MA_INVALID_ARGS if the volume factor is not within the range of [0, 1].
5365 Safe. This just sets a local member of the device object.
5370 Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied.
5375 This applies the volume factor across all channels.
5377 This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream.
5382 ma_device_get_master_volume()
5383 ma_device_set_master_volume_gain_db()
5384 ma_device_get_master_volume_gain_db()
5386 MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume);
5389 Retrieves the master volume factor for the device.
5395 A pointer to the device whose volume factor is being retrieved.
5398 A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1].
5403 MA_SUCCESS if successful.
5404 MA_INVALID_ARGS if pDevice is NULL.
5405 MA_INVALID_ARGS if pVolume is NULL.
5410 Safe. This just a simple member retrieval.
5420 If an error occurs, `*pVolume` will be set to 0.
5425 ma_device_set_master_volume()
5426 ma_device_set_master_volume_gain_db()
5427 ma_device_get_master_volume_gain_db()
5429 MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume);
5432 Sets the master volume for the device as gain in decibels.
5434 A gain of 0 is full volume, whereas a gain of < 0 will decrease the volume.
5440 A pointer to the device whose gain is being set.
5443 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.
5448 MA_SUCCESS if the volume was set successfully.
5449 MA_INVALID_ARGS if pDevice is NULL.
5450 MA_INVALID_ARGS if the gain is > 0.
5455 Safe. This just sets a local member of the device object.
5460 Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied.
5465 This applies the gain across all channels.
5467 This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream.
5472 ma_device_get_master_volume_gain_db()
5473 ma_device_set_master_volume()
5474 ma_device_get_master_volume()
5476 MA_API ma_result ma_device_set_master_gain_db(ma_device* pDevice, float gainDB);
5479 Retrieves the master gain in decibels.
5485 A pointer to the device whose gain is being retrieved.
5488 A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0.
5493 MA_SUCCESS if successful.
5494 MA_INVALID_ARGS if pDevice is NULL.
5495 MA_INVALID_ARGS if pGainDB is NULL.
5500 Safe. This just a simple member retrieval.
5510 If an error occurs, `*pGainDB` will be set to 0.
5515 ma_device_set_master_volume_gain_db()
5516 ma_device_set_master_volume()
5517 ma_device_get_master_volume()
5519 MA_API ma_result ma_device_get_master_gain_db(ma_device* pDevice, float* pGainDB);
5523 Called from the data callback of asynchronous backends to allow miniaudio to process the data and fire the miniaudio data callback.
5529 A pointer to device whose processing the data callback.
5532 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
5533 this can be NULL, in which case pInput must not be NULL.
5536 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
5537 NULL, in which case `pOutput` must not be NULL.
5540 The number of frames being processed.
5545 MA_SUCCESS if successful; any other result code otherwise.
5550 This function should only ever be called from the internal data callback of the backend. It is safe to call this simultaneously between a
5551 playback and capture device in duplex setups.
5556 Do not call this from the miniaudio data callback. It should only ever be called from the internal data callback of the backend.
5561 If both `pOutput` and `pInput` are NULL, and error will be returned. In duplex scenarios, both `pOutput` and `pInput` can be non-NULL, in
5562 which case `pInput` will be processed first, followed by `pOutput`.
5564 If you are implementing a custom backend, and that backend uses a callback for data delivery, you'll need to call this from inside that
5567 MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
5571 Calculates an appropriate buffer size from a descriptor, native sample rate and performance profile.
5573 This function is used by backends for helping determine an appropriately sized buffer to use with
5574 the device depending on the values of `periodSizeInFrames` and `periodSizeInMilliseconds` in the
5575 `pDescriptor` object. Since buffer size calculations based on time depends on the sample rate, a
5576 best guess at the device's native sample rate is also required which is where `nativeSampleRate`
5577 comes in. In addition, the performance profile is also needed for cases where both the period size
5578 in frames and milliseconds are both zero.
5584 A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members
5585 will be used for the calculation of the buffer size.
5587 nativeSampleRate (in)
5588 The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of
5589 `pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which
5590 case a sample rate is required to convert to a size in frames.
5592 performanceProfile (in)
5593 When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are
5594 zero, miniaudio will fall back to a buffer size based on the performance profile. The profile
5595 to use for this calculation is determine by this parameter.
5600 The calculated buffer size in frames.
5605 This is safe so long as nothing modifies `pDescriptor` at the same time. However, this function
5606 should only ever be called from within the backend's device initialization routine and therefore
5607 shouldn't have any multithreading concerns.
5612 This is safe to call within the data callback, but there is no reason to ever do this.
5617 If `nativeSampleRate` is zero, this function will fall back to `pDescriptor->sampleRate`. If that
5618 is also zero, `MA_DEFAULT_SAMPLE_RATE` will be used instead.
5620 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile);
5625 Retrieves a friendly name for a backend.
5627 MA_API const char* ma_get_backend_name(ma_backend backend);
5630 Determines whether or not the given backend is available by the compilation environment.
5632 MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend);
5635 Retrieves compile-time enabled backends.
5640 pBackends (out, optional)
5641 A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting
5642 the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends.
5645 The capacity of the `pBackends` buffer.
5648 A pointer to the variable that will receive the enabled backend count.
5653 MA_SUCCESS if successful.
5654 MA_INVALID_ARGS if `pBackendCount` is NULL.
5655 MA_NO_SPACE if the capacity of `pBackends` is not large enough.
5657 If `MA_NO_SPACE` is returned, the `pBackends` buffer will be filled with `*pBackendCount` values.
5672 If you want to retrieve the number of backends so you can determine the capacity of `pBackends` buffer, you can call
5673 this function with `pBackends` set to NULL.
5675 This will also enumerate the null backend. If you don't want to include this you need to check for `ma_backend_null`
5676 when you enumerate over the returned backends and handle it appropriately. Alternatively, you can disable it at
5677 compile time with `MA_NO_NULL`.
5679 The returned backends are determined based on compile time settings, not the platform it's currently running on. For
5680 example, PulseAudio will be returned if it was enabled at compile time, even when the user doesn't actually have
5681 PulseAudio installed.
5686 The example below retrieves the enabled backend count using a fixed sized buffer allocated on the stack. The buffer is
5687 given a capacity of `MA_BACKEND_COUNT` which will guarantee it'll be large enough to store all available backends.
5688 Since `MA_BACKEND_COUNT` is always a relatively small value, this should be suitable for most scenarios.
5691 ma_backend enabledBackends[MA_BACKEND_COUNT];
5692 size_t enabledBackendCount;
5694 result = ma_get_enabled_backends(enabledBackends, MA_BACKEND_COUNT, &enabledBackendCount);
5695 if (result != MA_SUCCESS) {
5696 // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid.
5703 ma_is_backend_enabled()
5705 MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount);
5708 Determines whether or not loopback mode is support by a backend.
5710 MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend);
5712 #endif /* MA_NO_DEVICE_IO */
5715 #ifndef MA_NO_THREADING
5720 MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock);
5723 Locks a spinlock, but does not yield() when looping.
5725 MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock);
5730 MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock);
5736 A mutex must be created from a valid context. A mutex is initially unlocked.
5738 MA_API ma_result ma_mutex_init(ma_mutex* pMutex);
5743 MA_API void ma_mutex_uninit(ma_mutex* pMutex);
5746 Locks a mutex with an infinite timeout.
5748 MA_API void ma_mutex_lock(ma_mutex* pMutex);
5753 MA_API void ma_mutex_unlock(ma_mutex* pMutex);
5757 Initializes an auto-reset event.
5759 MA_API ma_result ma_event_init(ma_event* pEvent);
5762 Uninitializes an auto-reset event.
5764 MA_API void ma_event_uninit(ma_event* pEvent);
5767 Waits for the specified auto-reset event to become signalled.
5769 MA_API ma_result ma_event_wait(ma_event* pEvent);
5772 Signals the specified auto-reset event.
5774 MA_API ma_result ma_event_signal(ma_event* pEvent);
5775 #endif /* MA_NO_THREADING */
5778 /************************************************************************************************************************************************************
5782 ************************************************************************************************************************************************************/
5785 Adjust buffer size based on a scaling factor.
5787 This just multiplies the base size by the scaling factor, making sure it's a size of at least 1.
5789 MA_API ma_uint32 ma_scale_buffer_size(ma_uint32 baseBufferSize, float scale);
5792 Calculates a buffer size in milliseconds from the specified number of frames and sample rate.
5794 MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate);
5797 Calculates a buffer size in frames from the specified number of milliseconds and sample rate.
5799 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate);
5802 Copies PCM frames from one buffer to another.
5804 MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels);
5807 Copies silent frames into the given buffer.
5811 For all formats except `ma_format_u8`, the output buffer will be filled with 0. For `ma_format_u8` it will be filled with 128. The reason for this is that it
5812 makes more sense for the purpose of mixing to initialize it to the center point.
5814 MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels);
5815 static 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); }
5819 Offsets a pointer by the specified number of PCM frames.
5821 MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels);
5822 MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels);
5823 static MA_INLINE float* ma_offset_pcm_frames_ptr_f32(float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (float*)ma_offset_pcm_frames_ptr((void*)p, offsetInFrames, ma_format_f32, channels); }
5824 static MA_INLINE const float* ma_offset_pcm_frames_const_ptr_f32(const float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (const float*)ma_offset_pcm_frames_const_ptr((const void*)p, offsetInFrames, ma_format_f32, channels); }
5830 MA_API void ma_clip_samples_f32(float* p, ma_uint64 sampleCount);
5831 static MA_INLINE void ma_clip_pcm_frames_f32(float* p, ma_uint64 frameCount, ma_uint32 channels) { ma_clip_samples_f32(p, frameCount*channels); }
5834 Helper for applying a volume factor to samples.
5836 Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place.
5838 MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor);
5839 MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor);
5840 MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor);
5841 MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor);
5842 MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor);
5844 MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor);
5845 MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor);
5846 MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor);
5847 MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor);
5848 MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor);
5850 MA_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);
5851 MA_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);
5852 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
5853 MA_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);
5854 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
5855 MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor);
5857 MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
5858 MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
5859 MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
5860 MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
5861 MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
5862 MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor);
5866 Helper for converting a linear factor to gain in decibels.
5868 MA_API float ma_factor_to_gain_db(float factor);
5871 Helper for converting gain in decibels to a linear factor.
5873 MA_API float ma_gain_db_to_factor(float gain);
5876 typedef void ma_data_source;
5880 ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
5881 ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex);
5882 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. */
5883 ma_result (* onUnmap)(ma_data_source* pDataSource, ma_uint64 frameCount);
5884 ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
5885 ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor);
5886 ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength);
5887 } ma_data_source_callbacks;
5889 MA_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. */
5890 MA_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); */
5891 MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex);
5892 MA_API ma_result ma_data_source_map(ma_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount);
5893 MA_API ma_result ma_data_source_unmap(ma_data_source* pDataSource, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */
5894 MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
5895 MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor);
5896 MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */
5903 ma_data_source_callbacks ds;
5907 ma_uint64 sizeInFrames;
5909 } ma_audio_buffer_ref;
5911 MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef);
5912 MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames);
5913 MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop);
5914 MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex);
5915 MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount);
5916 MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */
5917 MA_API ma_result ma_audio_buffer_ref_at_end(ma_audio_buffer_ref* pAudioBufferRef);
5918 MA_API ma_result ma_audio_buffer_ref_get_available_frames(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames);
5926 ma_uint64 sizeInFrames;
5927 const void* pData; /* If set to NULL, will allocate a block of memory for you. */
5928 ma_allocation_callbacks allocationCallbacks;
5929 } ma_audio_buffer_config;
5931 MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks);
5935 ma_audio_buffer_ref ref;
5936 ma_allocation_callbacks allocationCallbacks;
5937 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(). */
5938 ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */
5941 MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer);
5942 MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer);
5943 MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */
5944 MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer);
5945 MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer);
5946 MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop);
5947 MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex);
5948 MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount);
5949 MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */
5950 MA_API ma_result ma_audio_buffer_at_end(ma_audio_buffer* pAudioBuffer);
5951 MA_API ma_result ma_audio_buffer_get_available_frames(ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames);
5955 /************************************************************************************************************************************************************
5960 The VFS object (virtual file system) is what's used to customize file access. This is useful in cases where stdio FILE* based APIs may not be entirely
5961 appropriate for a given situation.
5963 ************************************************************************************************************************************************************/
5964 typedef void ma_vfs;
5965 typedef ma_handle ma_vfs_file;
5967 #define MA_OPEN_MODE_READ 0x00000001
5968 #define MA_OPEN_MODE_WRITE 0x00000002
5972 ma_seek_origin_start,
5973 ma_seek_origin_current,
5974 ma_seek_origin_end /* Not used by decoders. */
5979 ma_uint64 sizeInBytes;
5984 ma_result (* onOpen) (ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
5985 ma_result (* onOpenW)(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
5986 ma_result (* onClose)(ma_vfs* pVFS, ma_vfs_file file);
5987 ma_result (* onRead) (ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead);
5988 ma_result (* onWrite)(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten);
5989 ma_result (* onSeek) (ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin);
5990 ma_result (* onTell) (ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor);
5991 ma_result (* onInfo) (ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo);
5994 MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
5995 MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
5996 MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file);
5997 MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead);
5998 MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten);
5999 MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin);
6000 MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor);
6001 MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo);
6002 MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks);
6006 ma_vfs_callbacks cb;
6007 ma_allocation_callbacks allocationCallbacks; /* Only used for the wchar_t version of open() on non-Windows platforms. */
6010 MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks);
6015 #if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)
6018 ma_resource_format_wav
6019 } ma_resource_format;
6022 /************************************************************************************************************************************************************
6027 Decoders are independent of the main device API. Decoding APIs can be called freely inside the device's data callback, but they are not thread safe unless
6028 you do your own synchronization.
6030 ************************************************************************************************************************************************************/
6031 #ifndef MA_NO_DECODING
6032 typedef struct ma_decoder ma_decoder;
6034 typedef size_t (* ma_decoder_read_proc) (ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead); /* Returns the number of bytes read. */
6035 typedef ma_bool32 (* ma_decoder_seek_proc) (ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin); /* Origin will never be ma_seek_origin_end. */
6036 typedef ma_uint64 (* ma_decoder_read_pcm_frames_proc) (ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount); /* Returns the number of frames read. Output data is in internal format. */
6037 typedef ma_result (* ma_decoder_seek_to_pcm_frame_proc) (ma_decoder* pDecoder, ma_uint64 frameIndex);
6038 typedef ma_result (* ma_decoder_uninit_proc) (ma_decoder* pDecoder);
6039 typedef ma_uint64 (* ma_decoder_get_length_in_pcm_frames_proc)(ma_decoder* pDecoder);
6043 ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */
6044 ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */
6045 ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */
6046 ma_channel channelMap[MA_MAX_CHANNELS];
6047 ma_channel_mix_mode channelMixMode;
6048 ma_dither_mode ditherMode;
6051 ma_resample_algorithm algorithm;
6061 ma_allocation_callbacks allocationCallbacks;
6062 } ma_decoder_config;
6066 ma_data_source_callbacks ds;
6067 ma_decoder_read_proc onRead;
6068 ma_decoder_seek_proc onSeek;
6070 ma_uint64 readPointerInBytes; /* In internal encoded data. */
6071 ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */
6072 ma_format internalFormat;
6073 ma_uint32 internalChannels;
6074 ma_uint32 internalSampleRate;
6075 ma_channel internalChannelMap[MA_MAX_CHANNELS];
6076 ma_format outputFormat;
6077 ma_uint32 outputChannels;
6078 ma_uint32 outputSampleRate;
6079 ma_channel outputChannelMap[MA_MAX_CHANNELS];
6080 ma_data_converter converter; /* <-- Data conversion is achieved by running frames through this. */
6081 ma_allocation_callbacks allocationCallbacks;
6082 ma_decoder_read_pcm_frames_proc onReadPCMFrames;
6083 ma_decoder_seek_to_pcm_frame_proc onSeekToPCMFrame;
6084 ma_decoder_uninit_proc onUninit;
6085 ma_decoder_get_length_in_pcm_frames_proc onGetLengthInPCMFrames;
6086 void* pInternalDecoder; /* <-- The drwav/drflac/stb_vorbis/etc. objects. */
6096 const ma_uint8* pData;
6098 size_t currentReadPos;
6099 } memory; /* Only used for decoders that were opened against a block of memory. */
6103 MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate);
6105 MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6106 MA_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);
6107 MA_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);
6108 MA_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);
6109 MA_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);
6110 MA_API ma_result ma_decoder_init_raw(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder);
6112 MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6113 MA_API ma_result ma_decoder_init_memory_wav(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6114 MA_API ma_result ma_decoder_init_memory_flac(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6115 MA_API ma_result ma_decoder_init_memory_mp3(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6116 MA_API ma_result ma_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6117 MA_API ma_result ma_decoder_init_memory_raw(const void* pData, size_t dataSize, const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder);
6119 MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6120 MA_API ma_result ma_decoder_init_vfs_wav(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6121 MA_API ma_result ma_decoder_init_vfs_flac(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6122 MA_API ma_result ma_decoder_init_vfs_mp3(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6123 MA_API ma_result ma_decoder_init_vfs_vorbis(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6125 MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6126 MA_API ma_result ma_decoder_init_vfs_wav_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6127 MA_API ma_result ma_decoder_init_vfs_flac_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6128 MA_API ma_result ma_decoder_init_vfs_mp3_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6129 MA_API ma_result ma_decoder_init_vfs_vorbis_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6131 MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6132 MA_API ma_result ma_decoder_init_file_wav(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6133 MA_API ma_result ma_decoder_init_file_flac(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6134 MA_API ma_result ma_decoder_init_file_mp3(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6135 MA_API ma_result ma_decoder_init_file_vorbis(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6137 MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6138 MA_API ma_result ma_decoder_init_file_wav_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6139 MA_API ma_result ma_decoder_init_file_flac_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6140 MA_API ma_result ma_decoder_init_file_mp3_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6141 MA_API ma_result ma_decoder_init_file_vorbis_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6143 MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder);
6146 Retrieves the current position of the read cursor in PCM frames.
6148 MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor);
6151 Retrieves the length of the decoder in PCM frames.
6153 Do not call this on streams of an undefined length, such as internet radio.
6155 If the length is unknown or an error occurs, 0 will be returned.
6157 This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio
6160 For MP3's, this will decode the entire file. Do not call this in time critical scenarios.
6162 This function is not thread safe without your own synchronization.
6164 MA_API ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder);
6167 Reads PCM frames from the given decoder.
6169 This is not thread safe without your own synchronization.
6171 MA_API ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount);
6174 Seeks to a PCM frame based on it's absolute index.
6176 This is not thread safe without your own synchronization.
6178 MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex);
6181 Retrieves the number of frames that can be read before reaching the end.
6183 This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in
6184 particular ensuring you do not call it on streams of an undefined length, such as internet radio.
6186 If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be
6189 MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames);
6192 Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input,
6193 pConfig should be set to what you want. On output it will be set to what you got.
6195 MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
6196 MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
6197 MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
6199 #endif /* MA_NO_DECODING */
6202 /************************************************************************************************************************************************************
6207 Encoders do not perform any format conversion for you. If your target format does not support the format, and error will be returned.
6209 ************************************************************************************************************************************************************/
6210 #ifndef MA_NO_ENCODING
6211 typedef struct ma_encoder ma_encoder;
6213 typedef size_t (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite); /* Returns the number of bytes written. */
6214 typedef ma_bool32 (* ma_encoder_seek_proc) (ma_encoder* pEncoder, int byteOffset, ma_seek_origin origin);
6215 typedef ma_result (* ma_encoder_init_proc) (ma_encoder* pEncoder);
6216 typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder);
6217 typedef ma_uint64 (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount);
6221 ma_resource_format resourceFormat;
6224 ma_uint32 sampleRate;
6225 ma_allocation_callbacks allocationCallbacks;
6226 } ma_encoder_config;
6228 MA_API ma_encoder_config ma_encoder_config_init(ma_resource_format resourceFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate);
6232 ma_encoder_config config;
6233 ma_encoder_write_proc onWrite;
6234 ma_encoder_seek_proc onSeek;
6235 ma_encoder_init_proc onInit;
6236 ma_encoder_uninit_proc onUninit;
6237 ma_encoder_write_pcm_frames_proc onWritePCMFrames;
6239 void* pInternalEncoder; /* <-- The drwav/drflac/stb_vorbis/etc. objects. */
6240 void* pFile; /* FILE*. Only used when initialized with ma_encoder_init_file(). */
6243 MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
6244 MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
6245 MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
6246 MA_API void ma_encoder_uninit(ma_encoder* pEncoder);
6247 MA_API ma_uint64 ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount);
6249 #endif /* MA_NO_ENCODING */
6252 /************************************************************************************************************************************************************
6256 ************************************************************************************************************************************************************/
6257 #ifndef MA_NO_GENERATION
6260 ma_waveform_type_sine,
6261 ma_waveform_type_square,
6262 ma_waveform_type_triangle,
6263 ma_waveform_type_sawtooth
6270 ma_uint32 sampleRate;
6271 ma_waveform_type type;
6274 } ma_waveform_config;
6276 MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency);
6280 ma_data_source_callbacks ds;
6281 ma_waveform_config config;
6286 MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform);
6287 MA_API ma_uint64 ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount);
6288 MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex);
6289 MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude);
6290 MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency);
6291 MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type);
6292 MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate);
6296 ma_noise_type_white,
6298 ma_noise_type_brownian
6308 ma_bool32 duplicateChannels;
6311 MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude);
6315 ma_data_source_callbacks ds;
6316 ma_noise_config config;
6322 double bin[MA_MAX_CHANNELS][16];
6323 double accumulation[MA_MAX_CHANNELS];
6324 ma_uint32 counter[MA_MAX_CHANNELS];
6328 double accumulation[MA_MAX_CHANNELS];
6333 MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, ma_noise* pNoise);
6334 MA_API ma_uint64 ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount);
6335 MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude);
6336 MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed);
6337 MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type);
6339 #endif /* MA_NO_GENERATION */
6344 #endif /* miniaudio_h */
6348 /************************************************************************************************************************************************************
6349 *************************************************************************************************************************************************************
6353 *************************************************************************************************************************************************************
6354 ************************************************************************************************************************************************************/
6355 #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
6360 #include <limits.h> /* For INT_MAX */
6361 #include <math.h> /* sin(), etc. */
6365 #if !defined(_MSC_VER) && !defined(__DMC__)
6366 #include <strings.h> /* For strcasecmp(). */
6367 #include <wchar.h> /* For wcslen(), wcsrtombs() */
6371 #include <windows.h>
6373 #include <stdlib.h> /* For malloc(), free(), wcstombs(). */
6374 #include <string.h> /* For memset() */
6376 #include <sys/time.h> /* select() (used for ma_sleep()). */
6379 #include <sys/stat.h> /* For fstat(), etc. */
6381 #ifdef MA_EMSCRIPTEN
6382 #include <emscripten/emscripten.h>
6385 #if !defined(MA_64BIT) && !defined(MA_32BIT)
6395 #if !defined(MA_64BIT) && !defined(MA_32BIT)
6405 #if !defined(MA_64BIT) && !defined(MA_32BIT)
6407 #if INTPTR_MAX == INT64_MAX
6414 /* Architecture Detection */
6415 #if defined(__x86_64__) || defined(_M_X64)
6417 #elif defined(__i386) || defined(_M_IX86)
6419 #elif defined(__arm__) || defined(_M_ARM)
6423 /* Cannot currently support AVX-512 if AVX is disabled. */
6424 #if !defined(MA_NO_AVX512) && defined(MA_NO_AVX2)
6425 #define MA_NO_AVX512
6428 /* Intrinsics Support */
6429 #if defined(MA_X64) || defined(MA_X86)
6430 #if defined(_MSC_VER) && !defined(__clang__)
6432 #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */
6433 #define MA_SUPPORT_SSE2
6435 /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */
6436 /* #define MA_SUPPORT_AVX*/
6438 #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */
6439 #define MA_SUPPORT_AVX2
6441 #if _MSC_VER >= 1910 && !defined(MA_NO_AVX512) /* 2017 */
6442 #define MA_SUPPORT_AVX512
6445 /* Assume GNUC-style. */
6446 #if defined(__SSE2__) && !defined(MA_NO_SSE2)
6447 #define MA_SUPPORT_SSE2
6449 /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/
6450 /* #define MA_SUPPORT_AVX*/
6452 #if defined(__AVX2__) && !defined(MA_NO_AVX2)
6453 #define MA_SUPPORT_AVX2
6455 #if defined(__AVX512F__) && !defined(MA_NO_AVX512)
6456 #define MA_SUPPORT_AVX512
6460 /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */
6461 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
6462 #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include(<emmintrin.h>)
6463 #define MA_SUPPORT_SSE2
6465 /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include(<immintrin.h>)*/
6466 /* #define MA_SUPPORT_AVX*/
6468 #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include(<immintrin.h>)
6469 #define MA_SUPPORT_AVX2
6471 #if !defined(MA_SUPPORT_AVX512) && !defined(MA_NO_AVX512) && __has_include(<zmmintrin.h>)
6472 #define MA_SUPPORT_AVX512
6476 #if defined(MA_SUPPORT_AVX512)
6477 #include <immintrin.h> /* Not a mistake. Intentionally including <immintrin.h> instead of <zmmintrin.h> because otherwise the compiler will complain. */
6478 #elif defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX)
6479 #include <immintrin.h>
6480 #elif defined(MA_SUPPORT_SSE2)
6481 #include <emmintrin.h>
6486 #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
6487 #define MA_SUPPORT_NEON
6490 /* Fall back to looking for the #include file. */
6491 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
6492 #if !defined(MA_SUPPORT_NEON) && !defined(MA_NO_NEON) && __has_include(<arm_neon.h>)
6493 #define MA_SUPPORT_NEON
6497 #if defined(MA_SUPPORT_NEON)
6498 #include <arm_neon.h>
6502 /* Begin globally disabled warnings. */
6503 #if defined(_MSC_VER)
6504 #pragma warning(push)
6505 #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */
6508 #if defined(MA_X64) || defined(MA_X86)
6509 #if defined(_MSC_VER) && !defined(__clang__)
6510 #if _MSC_VER >= 1400
6512 static MA_INLINE void ma_cpuid(int info[4], int fid)
6520 #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219)
6521 static MA_INLINE unsigned __int64 ma_xgetbv(int reg)
6523 return _xgetbv(reg);
6526 #define MA_NO_XGETBV
6528 #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID)
6529 static MA_INLINE void ma_cpuid(int info[4], int fid)
6532 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
6533 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
6534 supporting different assembly dialects.
6536 What's basically happening is that we're saving and restoring the ebx register manually.
6538 #if defined(DRFLAC_X86) && defined(__PIC__)
6539 __asm__ __volatile__ (
6540 "xchg{l} {%%}ebx, %k1;"
6542 "xchg{l} {%%}ebx, %k1;"
6543 : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
6546 __asm__ __volatile__ (
6547 "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
6552 static MA_INLINE ma_uint64 ma_xgetbv(int reg)
6557 __asm__ __volatile__ (
6558 "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg)
6561 return ((ma_uint64)hi << 32) | (ma_uint64)lo;
6565 #define MA_NO_XGETBV
6569 #define MA_NO_XGETBV
6572 static MA_INLINE ma_bool32 ma_has_sse2(void)
6574 #if defined(MA_SUPPORT_SSE2)
6575 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2)
6577 return MA_TRUE; /* 64-bit targets always support SSE2. */
6578 #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
6579 return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */
6581 #if defined(MA_NO_CPUID)
6586 return (info[3] & (1 << 26)) != 0;
6590 return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */
6593 return MA_FALSE; /* No compiler support. */
6598 static MA_INLINE ma_bool32 ma_has_avx()
6600 #if defined(MA_SUPPORT_AVX)
6601 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX)
6602 #if defined(_AVX_) || defined(__AVX__)
6603 return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */
6605 /* AVX requires both CPU and OS support. */
6606 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
6611 if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) {
6612 ma_uint64 xrc = ma_xgetbv(0);
6613 if ((xrc & 0x06) == 0x06) {
6624 return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */
6627 return MA_FALSE; /* No compiler support. */
6632 static MA_INLINE ma_bool32 ma_has_avx2(void)
6634 #if defined(MA_SUPPORT_AVX2)
6635 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2)
6636 #if defined(_AVX2_) || defined(__AVX2__)
6637 return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */
6639 /* AVX2 requires both CPU and OS support. */
6640 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
6647 if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) {
6648 ma_uint64 xrc = ma_xgetbv(0);
6649 if ((xrc & 0x06) == 0x06) {
6660 return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */
6663 return MA_FALSE; /* No compiler support. */
6667 static MA_INLINE ma_bool32 ma_has_avx512f(void)
6669 #if defined(MA_SUPPORT_AVX512)
6670 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX512)
6671 #if defined(__AVX512F__)
6672 return MA_TRUE; /* If the compiler is allowed to freely generate AVX-512F code we can assume support. */
6674 /* AVX-512 requires both CPU and OS support. */
6675 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
6682 if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 16)) != 0)) {
6683 ma_uint64 xrc = ma_xgetbv(0);
6684 if ((xrc & 0xE6) == 0xE6) {
6695 return MA_FALSE; /* AVX-512F is only supported on x86 and x64 architectures. */
6698 return MA_FALSE; /* No compiler support. */
6702 static MA_INLINE ma_bool32 ma_has_neon(void)
6704 #if defined(MA_SUPPORT_NEON)
6705 #if defined(MA_ARM) && !defined(MA_NO_NEON)
6706 #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
6707 return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */
6709 /* TODO: Runtime check. */
6713 return MA_FALSE; /* NEON is only supported on ARM architectures. */
6716 return MA_FALSE; /* No compiler support. */
6720 #define MA_SIMD_NONE 0
6721 #define MA_SIMD_SSE2 1
6722 #define MA_SIMD_AVX2 2
6723 #define MA_SIMD_NEON 3
6725 #ifndef MA_PREFERRED_SIMD
6726 # if defined(MA_SUPPORT_SSE2) && defined(MA_PREFER_SSE2)
6727 #define MA_PREFERRED_SIMD MA_SIMD_SSE2
6728 #elif defined(MA_SUPPORT_AVX2) && defined(MA_PREFER_AVX2)
6729 #define MA_PREFERRED_SIMD MA_SIMD_AVX2
6730 #elif defined(MA_SUPPORT_NEON) && defined(MA_PREFER_NEON)
6731 #define MA_PREFERRED_SIMD MA_SIMD_NEON
6733 #define MA_PREFERRED_SIMD MA_SIMD_NONE
6737 #if defined(__has_builtin)
6738 #define MA_COMPILER_HAS_BUILTIN(x) __has_builtin(x)
6740 #define MA_COMPILER_HAS_BUILTIN(x) 0
6744 #if MA_COMPILER_HAS_BUILTIN(__builtin_assume)
6745 #define MA_ASSUME(x) __builtin_assume(x)
6746 #elif MA_COMPILER_HAS_BUILTIN(__builtin_unreachable)
6747 #define MA_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0)
6748 #elif defined(_MSC_VER)
6749 #define MA_ASSUME(x) __assume(x)
6751 #define MA_ASSUME(x) while(0)
6756 #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER)
6757 #define MA_RESTRICT __restrict
6763 #if defined(_MSC_VER) && _MSC_VER >= 1400
6764 #define MA_HAS_BYTESWAP16_INTRINSIC
6765 #define MA_HAS_BYTESWAP32_INTRINSIC
6766 #define MA_HAS_BYTESWAP64_INTRINSIC
6767 #elif defined(__clang__)
6768 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap16)
6769 #define MA_HAS_BYTESWAP16_INTRINSIC
6771 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap32)
6772 #define MA_HAS_BYTESWAP32_INTRINSIC
6774 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap64)
6775 #define MA_HAS_BYTESWAP64_INTRINSIC
6777 #elif defined(__GNUC__)
6778 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
6779 #define MA_HAS_BYTESWAP32_INTRINSIC
6780 #define MA_HAS_BYTESWAP64_INTRINSIC
6782 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
6783 #define MA_HAS_BYTESWAP16_INTRINSIC
6788 static MA_INLINE ma_bool32 ma_is_little_endian(void)
6790 #if defined(MA_X86) || defined(MA_X64)
6794 return (*(char*)&n) == 1;
6798 static MA_INLINE ma_bool32 ma_is_big_endian(void)
6800 return !ma_is_little_endian();
6804 static MA_INLINE ma_uint32 ma_swap_endian_uint32(ma_uint32 n)
6806 #ifdef MA_HAS_BYTESWAP32_INTRINSIC
6807 #if defined(_MSC_VER)
6808 return _byteswap_ulong(n);
6809 #elif defined(__GNUC__) || defined(__clang__)
6810 #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. */
6811 /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */
6813 __asm__ __volatile__ (
6814 #if defined(MA_64BIT)
6815 "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! */
6817 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
6822 return __builtin_bswap32(n);
6825 #error "This compiler does not support the byte swap intrinsic."
6828 return ((n & 0xFF000000) >> 24) |
6829 ((n & 0x00FF0000) >> 8) |
6830 ((n & 0x0000FF00) << 8) |
6831 ((n & 0x000000FF) << 24);
6836 #if !defined(MA_EMSCRIPTEN)
6838 static void ma_sleep__win32(ma_uint32 milliseconds)
6840 Sleep((DWORD)milliseconds);
6844 static void ma_sleep__posix(ma_uint32 milliseconds)
6846 #ifdef MA_EMSCRIPTEN
6848 MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */
6850 #if _POSIX_C_SOURCE >= 199309L
6852 ts.tv_sec = milliseconds / 1000;
6853 ts.tv_nsec = milliseconds % 1000 * 1000000;
6854 nanosleep(&ts, NULL);
6857 tv.tv_sec = milliseconds / 1000;
6858 tv.tv_usec = milliseconds % 1000 * 1000;
6859 select(0, NULL, NULL, NULL, &tv);
6865 static void ma_sleep(ma_uint32 milliseconds)
6868 ma_sleep__win32(milliseconds);
6871 ma_sleep__posix(milliseconds);
6876 static MA_INLINE void ma_yield()
6878 #if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
6880 #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(__clang__)
6881 #if _MSC_VER >= 1400
6884 #if defined(__DMC__)
6885 /* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */
6892 __asm__ __volatile__ ("pause");
6894 #elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__)
6896 #if defined(_MSC_VER)
6897 /* 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. */
6900 __asm__ __volatile__ ("yield"); /* ARMv6K/ARMv6T2 and above. */
6903 /* Unknown or unsupported architecture. No-op. */
6909 #ifndef MA_COINIT_VALUE
6910 #define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */
6916 #define MA_FLT_MAX FLT_MAX
6918 #define MA_FLT_MAX 3.402823466e+38F
6924 #define MA_PI 3.14159265358979323846264f
6927 #define MA_PI_D 3.14159265358979323846264
6930 #define MA_TAU 6.28318530717958647693f
6933 #define MA_TAU_D 6.28318530717958647693
6937 /* The default format when ma_format_unknown (0) is requested when initializing a device. */
6938 #ifndef MA_DEFAULT_FORMAT
6939 #define MA_DEFAULT_FORMAT ma_format_f32
6942 /* The default channel count to use when 0 is used when initializing a device. */
6943 #ifndef MA_DEFAULT_CHANNELS
6944 #define MA_DEFAULT_CHANNELS 2
6947 /* The default sample rate to use when 0 is used when initializing a device. */
6948 #ifndef MA_DEFAULT_SAMPLE_RATE
6949 #define MA_DEFAULT_SAMPLE_RATE 48000
6952 /* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */
6953 #ifndef MA_DEFAULT_PERIODS
6954 #define MA_DEFAULT_PERIODS 3
6957 /* The default period size in milliseconds for low latency mode. */
6958 #ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY
6959 #define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 10
6962 /* The default buffer size in milliseconds for conservative mode. */
6963 #ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE
6964 #define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100
6967 /* The default LPF filter order for linear resampling. Note that this is clamped to MA_MAX_FILTER_ORDER. */
6968 #ifndef MA_DEFAULT_RESAMPLER_LPF_ORDER
6969 #if MA_MAX_FILTER_ORDER >= 4
6970 #define MA_DEFAULT_RESAMPLER_LPF_ORDER 4
6972 #define MA_DEFAULT_RESAMPLER_LPF_ORDER MA_MAX_FILTER_ORDER
6977 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
6978 #pragma GCC diagnostic push
6979 #pragma GCC diagnostic ignored "-Wunused-variable"
6982 /* Standard sample rates, in order of priority. */
6983 static ma_uint32 g_maStandardSampleRatePriorities[] = {
6984 (ma_uint32)ma_standard_sample_rate_48000,
6985 (ma_uint32)ma_standard_sample_rate_44100,
6987 (ma_uint32)ma_standard_sample_rate_32000,
6988 (ma_uint32)ma_standard_sample_rate_24000,
6989 (ma_uint32)ma_standard_sample_rate_22050,
6991 (ma_uint32)ma_standard_sample_rate_88200,
6992 (ma_uint32)ma_standard_sample_rate_96000,
6993 (ma_uint32)ma_standard_sample_rate_176400,
6994 (ma_uint32)ma_standard_sample_rate_192000,
6996 (ma_uint32)ma_standard_sample_rate_16000,
6997 (ma_uint32)ma_standard_sample_rate_11025,
6998 (ma_uint32)ma_standard_sample_rate_8000,
7000 (ma_uint32)ma_standard_sample_rate_352800,
7001 (ma_uint32)ma_standard_sample_rate_384000
7004 static MA_INLINE ma_bool32 ma_is_standard_sample_rate(ma_uint32 sampleRate)
7006 ma_uint32 iSampleRate;
7008 for (iSampleRate = 0; iSampleRate < sizeof(g_maStandardSampleRatePriorities) / sizeof(g_maStandardSampleRatePriorities[0]); iSampleRate += 1) {
7009 if (g_maStandardSampleRatePriorities[iSampleRate] == sampleRate) {
7014 /* Getting here means the sample rate is not supported. */
7019 static ma_format g_maFormatPriorities[] = {
7020 ma_format_s16, /* Most common */
7023 /*ma_format_s24_32,*/ /* Clean alignment */
7026 ma_format_s24, /* Unclean alignment */
7028 ma_format_u8 /* Low quality */
7030 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
7031 #pragma GCC diagnostic pop
7035 MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)
7038 *pMajor = MA_VERSION_MAJOR;
7042 *pMinor = MA_VERSION_MINOR;
7046 *pRevision = MA_VERSION_REVISION;
7050 MA_API const char* ma_version_string(void)
7052 return MA_VERSION_STRING;
7056 /******************************************************************************
7058 Standard Library Stuff
7060 ******************************************************************************/
7063 #define MA_MALLOC(sz) HeapAlloc(GetProcessHeap(), 0, (sz))
7065 #define MA_MALLOC(sz) malloc((sz))
7071 #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)))
7073 #define MA_REALLOC(p, sz) realloc((p), (sz))
7079 #define MA_FREE(p) HeapFree(GetProcessHeap(), 0, (p))
7081 #define MA_FREE(p) free((p))
7085 #ifndef MA_ZERO_MEMORY
7087 #define MA_ZERO_MEMORY(p, sz) ZeroMemory((p), (sz))
7089 #define MA_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
7093 #ifndef MA_COPY_MEMORY
7095 #define MA_COPY_MEMORY(dst, src, sz) CopyMemory((dst), (src), (sz))
7097 #define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
7103 #define MA_ASSERT(condition) assert(condition)
7105 #define MA_ASSERT(condition) assert(condition)
7109 #define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p)))
7111 #define ma_countof(x) (sizeof(x) / sizeof(x[0]))
7112 #define ma_max(x, y) (((x) > (y)) ? (x) : (y))
7113 #define ma_min(x, y) (((x) < (y)) ? (x) : (y))
7114 #define ma_abs(x) (((x) > 0) ? (x) : -(x))
7115 #define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi)))
7116 #define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset))
7118 #define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels))
7120 static MA_INLINE double ma_sin(double x)
7122 /* TODO: Implement custom sin(x). */
7126 static MA_INLINE double ma_exp(double x)
7128 /* TODO: Implement custom exp(x). */
7132 static MA_INLINE double ma_log(double x)
7134 /* TODO: Implement custom log(x). */
7138 static MA_INLINE double ma_pow(double x, double y)
7140 /* TODO: Implement custom pow(x, y). */
7144 static MA_INLINE double ma_sqrt(double x)
7146 /* TODO: Implement custom sqrt(x). */
7151 static MA_INLINE double ma_cos(double x)
7153 return ma_sin((MA_PI_D*0.5) - x);
7156 static MA_INLINE double ma_log10(double x)
7158 return ma_log(x) * 0.43429448190325182765;
7161 static MA_INLINE float ma_powf(float x, float y)
7163 return (float)ma_pow((double)x, (double)y);
7166 static MA_INLINE float ma_log10f(float x)
7168 return (float)ma_log10((double)x);
7172 static MA_INLINE double ma_degrees_to_radians(double degrees)
7174 return degrees * 0.01745329252;
7177 static MA_INLINE double ma_radians_to_degrees(double radians)
7179 return radians * 57.295779512896;
7182 static MA_INLINE float ma_degrees_to_radians_f(float degrees)
7184 return degrees * 0.01745329252f;
7187 static MA_INLINE float ma_radians_to_degrees_f(float radians)
7189 return radians * 57.295779512896f;
7199 Not using symbolic constants for errors because I want to avoid #including errno.h
7201 MA_API int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
7208 if (dstSizeInBytes == 0) {
7216 for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
7220 if (i < dstSizeInBytes) {
7229 MA_API int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src)
7244 for (i = 0; i < dstCap && src[i] != '\0'; ++i) {
7258 MA_API int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
7266 if (dstSizeInBytes == 0) {
7275 if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */
7276 maxcount = dstSizeInBytes - 1;
7279 for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
7283 if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
7292 MA_API int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
7299 if (dstSizeInBytes == 0) {
7309 while (dstSizeInBytes > 0 && dst[0] != '\0') {
7311 dstSizeInBytes -= 1;
7314 if (dstSizeInBytes == 0) {
7315 return 22; /* Unterminated. */
7319 while (dstSizeInBytes > 0 && src[0] != '\0') {
7321 dstSizeInBytes -= 1;
7324 if (dstSizeInBytes > 0) {
7334 MA_API int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
7341 if (dstSizeInBytes == 0) {
7350 while (dstSizeInBytes > 0 && dst[0] != '\0') {
7352 dstSizeInBytes -= 1;
7355 if (dstSizeInBytes == 0) {
7356 return 22; /* Unterminated. */
7360 if (count == ((size_t)-1)) { /* _TRUNCATE */
7361 count = dstSizeInBytes - 1;
7364 while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) {
7366 dstSizeInBytes -= 1;
7370 if (dstSizeInBytes > 0) {
7380 MA_API int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix)
7383 unsigned int valueU;
7386 if (dst == NULL || dstSizeInBytes == 0) {
7389 if (radix < 2 || radix > 36) {
7394 sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */
7405 int remainder = valueU % radix;
7406 if (remainder > 9) {
7407 *dstEnd = (char)((remainder - 10) + 'a');
7409 *dstEnd = (char)(remainder + '0');
7413 dstSizeInBytes -= 1;
7415 } while (dstSizeInBytes > 0 && valueU > 0);
7417 if (dstSizeInBytes == 0) {
7419 return 22; /* Ran out of room in the output buffer. */
7424 dstSizeInBytes -= 1;
7427 if (dstSizeInBytes == 0) {
7429 return 22; /* Ran out of room in the output buffer. */
7435 /* At this point the string will be reversed. */
7437 while (dst < dstEnd) {
7449 MA_API int ma_strcmp(const char* str1, const char* str2)
7451 if (str1 == str2) return 0;
7453 /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */
7454 if (str1 == NULL) return -1;
7455 if (str2 == NULL) return 1;
7458 if (str1[0] == '\0') {
7461 if (str1[0] != str2[0]) {
7469 return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0];
7472 MA_API int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB)
7476 result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1);
7481 result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1);
7489 MA_API char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks)
7491 size_t sz = strlen(src)+1;
7492 char* dst = (char*)ma_malloc(sz, pAllocationCallbacks);
7497 ma_strcpy_s(dst, sz, src);
7502 MA_API wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks)
7504 size_t sz = wcslen(src)+1;
7505 wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks);
7510 ma_wcscpy_s(dst, sz, src);
7517 static ma_result ma_result_from_errno(int e)
7521 case 0: return MA_SUCCESS;
7523 case EPERM: return MA_INVALID_OPERATION;
7526 case ENOENT: return MA_DOES_NOT_EXIST;
7529 case ESRCH: return MA_DOES_NOT_EXIST;
7532 case EINTR: return MA_INTERRUPT;
7535 case EIO: return MA_IO_ERROR;
7538 case ENXIO: return MA_DOES_NOT_EXIST;
7541 case E2BIG: return MA_INVALID_ARGS;
7544 case ENOEXEC: return MA_INVALID_FILE;
7547 case EBADF: return MA_INVALID_FILE;
7550 case ECHILD: return MA_ERROR;
7553 case EAGAIN: return MA_UNAVAILABLE;
7556 case ENOMEM: return MA_OUT_OF_MEMORY;
7559 case EACCES: return MA_ACCESS_DENIED;
7562 case EFAULT: return MA_BAD_ADDRESS;
7565 case ENOTBLK: return MA_ERROR;
7568 case EBUSY: return MA_BUSY;
7571 case EEXIST: return MA_ALREADY_EXISTS;
7574 case EXDEV: return MA_ERROR;
7577 case ENODEV: return MA_DOES_NOT_EXIST;
7580 case ENOTDIR: return MA_NOT_DIRECTORY;
7583 case EISDIR: return MA_IS_DIRECTORY;
7586 case EINVAL: return MA_INVALID_ARGS;
7589 case ENFILE: return MA_TOO_MANY_OPEN_FILES;
7592 case EMFILE: return MA_TOO_MANY_OPEN_FILES;
7595 case ENOTTY: return MA_INVALID_OPERATION;
7598 case ETXTBSY: return MA_BUSY;
7601 case EFBIG: return MA_TOO_BIG;
7604 case ENOSPC: return MA_NO_SPACE;
7607 case ESPIPE: return MA_BAD_SEEK;
7610 case EROFS: return MA_ACCESS_DENIED;
7613 case EMLINK: return MA_TOO_MANY_LINKS;
7616 case EPIPE: return MA_BAD_PIPE;
7619 case EDOM: return MA_OUT_OF_RANGE;
7622 case ERANGE: return MA_OUT_OF_RANGE;
7625 case EDEADLK: return MA_DEADLOCK;
7628 case ENAMETOOLONG: return MA_PATH_TOO_LONG;
7631 case ENOLCK: return MA_ERROR;
7634 case ENOSYS: return MA_NOT_IMPLEMENTED;
7637 case ENOTEMPTY: return MA_DIRECTORY_NOT_EMPTY;
7640 case ELOOP: return MA_TOO_MANY_LINKS;
7643 case ENOMSG: return MA_NO_MESSAGE;
7646 case EIDRM: return MA_ERROR;
7649 case ECHRNG: return MA_ERROR;
7652 case EL2NSYNC: return MA_ERROR;
7655 case EL3HLT: return MA_ERROR;
7658 case EL3RST: return MA_ERROR;
7661 case ELNRNG: return MA_OUT_OF_RANGE;
7664 case EUNATCH: return MA_ERROR;
7667 case ENOCSI: return MA_ERROR;
7670 case EL2HLT: return MA_ERROR;
7673 case EBADE: return MA_ERROR;
7676 case EBADR: return MA_ERROR;
7679 case EXFULL: return MA_ERROR;
7682 case ENOANO: return MA_ERROR;
7685 case EBADRQC: return MA_ERROR;
7688 case EBADSLT: return MA_ERROR;
7691 case EBFONT: return MA_INVALID_FILE;
7694 case ENOSTR: return MA_ERROR;
7697 case ENODATA: return MA_NO_DATA_AVAILABLE;
7700 case ETIME: return MA_TIMEOUT;
7703 case ENOSR: return MA_NO_DATA_AVAILABLE;
7706 case ENONET: return MA_NO_NETWORK;
7709 case ENOPKG: return MA_ERROR;
7712 case EREMOTE: return MA_ERROR;
7715 case ENOLINK: return MA_ERROR;
7718 case EADV: return MA_ERROR;
7721 case ESRMNT: return MA_ERROR;
7724 case ECOMM: return MA_ERROR;
7727 case EPROTO: return MA_ERROR;
7730 case EMULTIHOP: return MA_ERROR;
7733 case EDOTDOT: return MA_ERROR;
7736 case EBADMSG: return MA_BAD_MESSAGE;
7739 case EOVERFLOW: return MA_TOO_BIG;
7742 case ENOTUNIQ: return MA_NOT_UNIQUE;
7745 case EBADFD: return MA_ERROR;
7748 case EREMCHG: return MA_ERROR;
7751 case ELIBACC: return MA_ACCESS_DENIED;
7754 case ELIBBAD: return MA_INVALID_FILE;
7757 case ELIBSCN: return MA_INVALID_FILE;
7760 case ELIBMAX: return MA_ERROR;
7763 case ELIBEXEC: return MA_ERROR;
7766 case EILSEQ: return MA_INVALID_DATA;
7769 case ERESTART: return MA_ERROR;
7772 case ESTRPIPE: return MA_ERROR;
7775 case EUSERS: return MA_ERROR;
7778 case ENOTSOCK: return MA_NOT_SOCKET;
7781 case EDESTADDRREQ: return MA_NO_ADDRESS;
7784 case EMSGSIZE: return MA_TOO_BIG;
7787 case EPROTOTYPE: return MA_BAD_PROTOCOL;
7790 case ENOPROTOOPT: return MA_PROTOCOL_UNAVAILABLE;
7792 #ifdef EPROTONOSUPPORT
7793 case EPROTONOSUPPORT: return MA_PROTOCOL_NOT_SUPPORTED;
7795 #ifdef ESOCKTNOSUPPORT
7796 case ESOCKTNOSUPPORT: return MA_SOCKET_NOT_SUPPORTED;
7799 case EOPNOTSUPP: return MA_INVALID_OPERATION;
7802 case EPFNOSUPPORT: return MA_PROTOCOL_FAMILY_NOT_SUPPORTED;
7805 case EAFNOSUPPORT: return MA_ADDRESS_FAMILY_NOT_SUPPORTED;
7808 case EADDRINUSE: return MA_ALREADY_IN_USE;
7810 #ifdef EADDRNOTAVAIL
7811 case EADDRNOTAVAIL: return MA_ERROR;
7814 case ENETDOWN: return MA_NO_NETWORK;
7817 case ENETUNREACH: return MA_NO_NETWORK;
7820 case ENETRESET: return MA_NO_NETWORK;
7823 case ECONNABORTED: return MA_NO_NETWORK;
7826 case ECONNRESET: return MA_CONNECTION_RESET;
7829 case ENOBUFS: return MA_NO_SPACE;
7832 case EISCONN: return MA_ALREADY_CONNECTED;
7835 case ENOTCONN: return MA_NOT_CONNECTED;
7838 case ESHUTDOWN: return MA_ERROR;
7841 case ETOOMANYREFS: return MA_ERROR;
7844 case ETIMEDOUT: return MA_TIMEOUT;
7847 case ECONNREFUSED: return MA_CONNECTION_REFUSED;
7850 case EHOSTDOWN: return MA_NO_HOST;
7853 case EHOSTUNREACH: return MA_NO_HOST;
7856 case EALREADY: return MA_IN_PROGRESS;
7859 case EINPROGRESS: return MA_IN_PROGRESS;
7862 case ESTALE: return MA_INVALID_FILE;
7865 case EUCLEAN: return MA_ERROR;
7868 case ENOTNAM: return MA_ERROR;
7871 case ENAVAIL: return MA_ERROR;
7874 case EISNAM: return MA_ERROR;
7877 case EREMOTEIO: return MA_IO_ERROR;
7880 case EDQUOT: return MA_NO_SPACE;
7883 case ENOMEDIUM: return MA_DOES_NOT_EXIST;
7886 case EMEDIUMTYPE: return MA_ERROR;
7889 case ECANCELED: return MA_CANCELLED;
7892 case ENOKEY: return MA_ERROR;
7895 case EKEYEXPIRED: return MA_ERROR;
7898 case EKEYREVOKED: return MA_ERROR;
7901 case EKEYREJECTED: return MA_ERROR;
7904 case EOWNERDEAD: return MA_ERROR;
7906 #ifdef ENOTRECOVERABLE
7907 case ENOTRECOVERABLE: return MA_ERROR;
7910 case ERFKILL: return MA_ERROR;
7913 case EHWPOISON: return MA_ERROR;
7915 default: return MA_ERROR;
7919 MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
7921 #if defined(_MSC_VER) && _MSC_VER >= 1400
7925 if (ppFile != NULL) {
7926 *ppFile = NULL; /* Safety. */
7929 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
7930 return MA_INVALID_ARGS;
7933 #if defined(_MSC_VER) && _MSC_VER >= 1400
7934 err = fopen_s(ppFile, pFilePath, pOpenMode);
7936 return ma_result_from_errno(err);
7939 #if defined(_WIN32) || defined(__APPLE__)
7940 *ppFile = fopen(pFilePath, pOpenMode);
7942 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
7943 *ppFile = fopen64(pFilePath, pOpenMode);
7945 *ppFile = fopen(pFilePath, pOpenMode);
7948 if (*ppFile == NULL) {
7949 ma_result result = ma_result_from_errno(errno);
7950 if (result == MA_SUCCESS) {
7951 result = MA_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */
7964 _wfopen() isn't always available in all compilation environments.
7967 * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back).
7968 * MinGW-64 (both 32- and 64-bit) seems to support it.
7969 * MinGW wraps it in !defined(__STRICT_ANSI__).
7970 * OpenWatcom wraps it in !defined(_NO_EXT_KEYS).
7972 This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs()
7973 fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support.
7976 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
7977 #define MA_HAS_WFOPEN
7981 MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks)
7983 if (ppFile != NULL) {
7984 *ppFile = NULL; /* Safety. */
7987 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
7988 return MA_INVALID_ARGS;
7991 #if defined(MA_HAS_WFOPEN)
7993 /* Use _wfopen() on Windows. */
7994 #if defined(_MSC_VER) && _MSC_VER >= 1400
7995 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
7997 return ma_result_from_errno(err);
8000 *ppFile = _wfopen(pFilePath, pOpenMode);
8001 if (*ppFile == NULL) {
8002 return ma_result_from_errno(errno);
8005 (void)pAllocationCallbacks;
8009 Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can
8010 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
8011 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.
8016 const wchar_t* pFilePathTemp = pFilePath;
8017 char* pFilePathMB = NULL;
8018 char pOpenModeMB[32] = {0};
8020 /* Get the length first. */
8021 MA_ZERO_OBJECT(&mbs);
8022 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
8023 if (lenMB == (size_t)-1) {
8024 return ma_result_from_errno(errno);
8027 pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks);
8028 if (pFilePathMB == NULL) {
8029 return MA_OUT_OF_MEMORY;
8032 pFilePathTemp = pFilePath;
8033 MA_ZERO_OBJECT(&mbs);
8034 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
8036 /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */
8040 if (pOpenMode[i] == 0) {
8041 pOpenModeMB[i] = '\0';
8045 pOpenModeMB[i] = (char)pOpenMode[i];
8050 *ppFile = fopen(pFilePathMB, pOpenModeMB);
8052 ma_free(pFilePathMB, pAllocationCallbacks);
8055 if (*ppFile == NULL) {
8065 static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes)
8067 #if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX
8068 MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes);
8070 while (sizeInBytes > 0) {
8071 ma_uint64 bytesToCopyNow = sizeInBytes;
8072 if (bytesToCopyNow > MA_SIZE_MAX) {
8073 bytesToCopyNow = MA_SIZE_MAX;
8076 MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */
8078 sizeInBytes -= bytesToCopyNow;
8079 dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow);
8080 src = (const void*)((const ma_uint8*)src + bytesToCopyNow);
8085 static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes)
8087 #if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX
8088 MA_ZERO_MEMORY(dst, (size_t)sizeInBytes);
8090 while (sizeInBytes > 0) {
8091 ma_uint64 bytesToZeroNow = sizeInBytes;
8092 if (bytesToZeroNow > MA_SIZE_MAX) {
8093 bytesToZeroNow = MA_SIZE_MAX;
8096 MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */
8098 sizeInBytes -= bytesToZeroNow;
8099 dst = (void*)((ma_uint8*)dst + bytesToZeroNow);
8105 /* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */
8106 static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x)
8119 static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x)
8121 return ma_next_power_of_2(x) >> 1;
8124 static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x)
8126 unsigned int prev = ma_prev_power_of_2(x);
8127 unsigned int next = ma_next_power_of_2(x);
8128 if ((next - x) > (x - prev)) {
8135 static MA_INLINE unsigned int ma_count_set_bits(unsigned int x)
8137 unsigned int count = 0;
8151 /* Clamps an f32 sample to -1..1 */
8152 static MA_INLINE float ma_clip_f32(float x)
8154 if (x < -1) return -1;
8155 if (x > +1) return +1;
8159 static MA_INLINE float ma_mix_f32(float x, float y, float a)
8161 return x*(1-a) + y*a;
8163 static MA_INLINE float ma_mix_f32_fast(float x, float y, float a)
8168 /*return x + (y - x)*a;*/
8171 #if defined(MA_SUPPORT_SSE2)
8172 static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a)
8174 return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a));
8177 #if defined(MA_SUPPORT_AVX2)
8178 static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a)
8180 return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a));
8183 #if defined(MA_SUPPORT_AVX512)
8184 static MA_INLINE __m512 ma_mix_f32_fast__avx512(__m512 x, __m512 y, __m512 a)
8186 return _mm512_add_ps(x, _mm512_mul_ps(_mm512_sub_ps(y, x), a));
8189 #if defined(MA_SUPPORT_NEON)
8190 static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a)
8192 return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a));
8197 static MA_INLINE double ma_mix_f64(double x, double y, double a)
8199 return x*(1-a) + y*a;
8201 static MA_INLINE double ma_mix_f64_fast(double x, double y, double a)
8203 return x + (y - x)*a;
8206 static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi)
8208 return lo + x*(hi-lo);
8213 Greatest common factor using Euclid's algorithm iteratively.
8215 static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b)
8232 Random Number Generation
8234 miniaudio uses the LCG random number generation algorithm. This is good enough for audio.
8236 Note that miniaudio's global LCG implementation uses global state which is _not_ thread-local. When this is called across
8237 multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough for
8238 miniaudio's purposes.
8240 #ifndef MA_DEFAULT_LCG_SEED
8241 #define MA_DEFAULT_LCG_SEED 4321
8244 #define MA_LCG_M 2147483647
8245 #define MA_LCG_A 48271
8248 static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */
8250 static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed)
8252 MA_ASSERT(pLCG != NULL);
8256 static MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG)
8258 pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M;
8262 static MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG)
8264 return (ma_uint32)ma_lcg_rand_s32(pLCG);
8267 static MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG)
8269 return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF);
8272 static MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG)
8274 return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF;
8277 static MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG)
8279 return (float)ma_lcg_rand_f64(pLCG);
8282 static MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi)
8284 return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi);
8287 static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi)
8293 return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1);
8298 static MA_INLINE void ma_seed(ma_int32 seed)
8300 ma_lcg_seed(&g_maLCG, seed);
8303 static MA_INLINE ma_int32 ma_rand_s32(void)
8305 return ma_lcg_rand_s32(&g_maLCG);
8308 static MA_INLINE ma_uint32 ma_rand_u32(void)
8310 return ma_lcg_rand_u32(&g_maLCG);
8313 static MA_INLINE double ma_rand_f64(void)
8315 return ma_lcg_rand_f64(&g_maLCG);
8318 static MA_INLINE float ma_rand_f32(void)
8320 return ma_lcg_rand_f32(&g_maLCG);
8323 static MA_INLINE float ma_rand_range_f32(float lo, float hi)
8325 return ma_lcg_rand_range_f32(&g_maLCG, lo, hi);
8328 static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi)
8330 return ma_lcg_rand_range_s32(&g_maLCG, lo, hi);
8334 static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax)
8336 return ma_rand_range_f32(ditherMin, ditherMax);
8339 static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax)
8341 float a = ma_rand_range_f32(ditherMin, 0);
8342 float b = ma_rand_range_f32(0, ditherMax);
8346 static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax)
8348 if (ditherMode == ma_dither_mode_rectangle) {
8349 return ma_dither_f32_rectangle(ditherMin, ditherMax);
8351 if (ditherMode == ma_dither_mode_triangle) {
8352 return ma_dither_f32_triangle(ditherMin, ditherMax);
8358 static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax)
8360 if (ditherMode == ma_dither_mode_rectangle) {
8361 ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax);
8364 if (ditherMode == ma_dither_mode_triangle) {
8365 ma_int32 a = ma_rand_range_s32(ditherMin, 0);
8366 ma_int32 b = ma_rand_range_s32(0, ditherMax);
8374 /**************************************************************************************************************************************************************
8378 **************************************************************************************************************************************************************/
8379 /* c89atomic.h begin */
8382 #if defined(__cplusplus)
8385 typedef signed char c89atomic_int8;
8386 typedef unsigned char c89atomic_uint8;
8387 typedef signed short c89atomic_int16;
8388 typedef unsigned short c89atomic_uint16;
8389 typedef signed int c89atomic_int32;
8390 typedef unsigned int c89atomic_uint32;
8391 #if defined(_MSC_VER)
8392 typedef signed __int64 c89atomic_int64;
8393 typedef unsigned __int64 c89atomic_uint64;
8395 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
8396 #pragma GCC diagnostic push
8397 #pragma GCC diagnostic ignored "-Wlong-long"
8398 #if defined(__clang__)
8399 #pragma GCC diagnostic ignored "-Wc++11-long-long"
8402 typedef signed long long c89atomic_int64;
8403 typedef unsigned long long c89atomic_uint64;
8404 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
8405 #pragma GCC diagnostic pop
8408 typedef int c89atomic_memory_order;
8409 typedef unsigned char c89atomic_bool;
8410 #if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT)
8413 #define C89ATOMIC_64BIT
8415 #define C89ATOMIC_32BIT
8419 #if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT)
8422 #define C89ATOMIC_64BIT
8424 #define C89ATOMIC_32BIT
8428 #if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT)
8430 #if INTPTR_MAX == INT64_MAX
8431 #define C89ATOMIC_64BIT
8433 #define C89ATOMIC_32BIT
8436 #if defined(__x86_64__) || defined(_M_X64)
8437 #define C89ATOMIC_X64
8438 #elif defined(__i386) || defined(_M_IX86)
8439 #define C89ATOMIC_X86
8440 #elif defined(__arm__) || defined(_M_ARM)
8441 #define C89ATOMIC_ARM
8443 #if defined(_MSC_VER)
8444 #define C89ATOMIC_INLINE __forceinline
8445 #elif defined(__GNUC__)
8446 #if defined(__STRICT_ANSI__)
8447 #define C89ATOMIC_INLINE __inline__ __attribute__((always_inline))
8449 #define C89ATOMIC_INLINE inline __attribute__((always_inline))
8451 #elif defined(__WATCOMC__) || defined(__DMC__)
8452 #define C89ATOMIC_INLINE __inline
8454 #define C89ATOMIC_INLINE
8456 #define C89ATOMIC_HAS_8
8457 #define C89ATOMIC_HAS_16
8458 #define C89ATOMIC_HAS_32
8459 #define C89ATOMIC_HAS_64
8460 #if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__)
8461 #define c89atomic_memory_order_relaxed 0
8462 #define c89atomic_memory_order_consume 1
8463 #define c89atomic_memory_order_acquire 2
8464 #define c89atomic_memory_order_release 3
8465 #define c89atomic_memory_order_acq_rel 4
8466 #define c89atomic_memory_order_seq_cst 5
8467 #if _MSC_VER < 1600 && defined(C89ATOMIC_32BIT)
8468 #define C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY
8471 #undef C89ATOMIC_HAS_8
8472 #undef C89ATOMIC_HAS_16
8474 #if !defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
8477 #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
8478 #if defined(C89ATOMIC_HAS_8)
8479 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired)
8481 c89atomic_uint8 result = 0;
8486 lock cmpxchg [ecx], dl
8492 #if defined(C89ATOMIC_HAS_16)
8493 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired)
8495 c89atomic_uint16 result = 0;
8500 lock cmpxchg [ecx], dx
8506 #if defined(C89ATOMIC_HAS_32)
8507 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired)
8509 c89atomic_uint32 result = 0;
8514 lock cmpxchg [ecx], edx
8520 #if defined(C89ATOMIC_HAS_64)
8521 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired)
8523 c89atomic_uint32 resultEAX = 0;
8524 c89atomic_uint32 resultEDX = 0;
8527 mov eax, dword ptr expected
8528 mov edx, dword ptr expected + 4
8529 mov ebx, dword ptr desired
8530 mov ecx, dword ptr desired + 4
8531 lock cmpxchg8b qword ptr [esi]
8535 return ((c89atomic_uint64)resultEDX << 32) | resultEAX;
8539 #if defined(C89ATOMIC_HAS_8)
8540 #define c89atomic_compare_and_swap_8( dst, expected, desired) (c89atomic_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected)
8542 #if defined(C89ATOMIC_HAS_16)
8543 #define c89atomic_compare_and_swap_16(dst, expected, desired) (c89atomic_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected)
8545 #if defined(C89ATOMIC_HAS_32)
8546 #define c89atomic_compare_and_swap_32(dst, expected, desired) (c89atomic_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected)
8548 #if defined(C89ATOMIC_HAS_64)
8549 #define c89atomic_compare_and_swap_64(dst, expected, desired) (c89atomic_uint64)_InterlockedCompareExchange64((volatile long long*)dst, (long long)desired, (long long)expected)
8552 #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
8553 #if defined(C89ATOMIC_HAS_8)
8554 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
8556 c89atomic_uint8 result = 0;
8567 #if defined(C89ATOMIC_HAS_16)
8568 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
8570 c89atomic_uint16 result = 0;
8581 #if defined(C89ATOMIC_HAS_32)
8582 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
8584 c89atomic_uint32 result = 0;
8589 lock xchg [ecx], eax
8596 #if defined(C89ATOMIC_HAS_8)
8597 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
8600 return (c89atomic_uint8)_InterlockedExchange8((volatile char*)dst, (char)src);
8603 #if defined(C89ATOMIC_HAS_16)
8604 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
8607 return (c89atomic_uint16)_InterlockedExchange16((volatile short*)dst, (short)src);
8610 #if defined(C89ATOMIC_HAS_32)
8611 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
8614 return (c89atomic_uint32)_InterlockedExchange((volatile long*)dst, (long)src);
8617 #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT)
8618 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
8621 return (c89atomic_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src);
8626 #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT)
8627 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
8629 c89atomic_uint64 oldValue;
8632 } while (c89atomic_compare_and_swap_64(dst, oldValue, src) != oldValue);
8637 #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
8638 #if defined(C89ATOMIC_HAS_8)
8639 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
8641 c89atomic_uint8 result = 0;
8652 #if defined(C89ATOMIC_HAS_16)
8653 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
8655 c89atomic_uint16 result = 0;
8666 #if defined(C89ATOMIC_HAS_32)
8667 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
8669 c89atomic_uint32 result = 0;
8674 lock xadd [ecx], eax
8681 #if defined(C89ATOMIC_HAS_8)
8682 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
8685 return (c89atomic_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src);
8688 #if defined(C89ATOMIC_HAS_16)
8689 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
8692 return (c89atomic_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src);
8695 #if defined(C89ATOMIC_HAS_32)
8696 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
8699 return (c89atomic_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src);
8702 #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT)
8703 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
8706 return (c89atomic_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src);
8711 #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT)
8712 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
8714 c89atomic_uint64 oldValue;
8715 c89atomic_uint64 newValue;
8718 newValue = oldValue + src;
8719 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
8724 #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
8725 static C89ATOMIC_INLINE void __stdcall c89atomic_thread_fence(c89atomic_memory_order order)
8733 #if defined(C89ATOMIC_X64)
8734 #define c89atomic_thread_fence(order) __faststorefence(), (void)order
8736 static C89ATOMIC_INLINE void c89atomic_thread_fence(c89atomic_memory_order order)
8738 volatile c89atomic_uint32 barrier = 0;
8739 c89atomic_fetch_add_explicit_32(&barrier, 0, order);
8743 #define c89atomic_compiler_fence() c89atomic_thread_fence(c89atomic_memory_order_seq_cst)
8744 #define c89atomic_signal_fence(order) c89atomic_thread_fence(order)
8745 #if defined(C89ATOMIC_HAS_8)
8746 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order)
8749 return c89atomic_compare_and_swap_8((c89atomic_uint8*)ptr, 0, 0);
8752 #if defined(C89ATOMIC_HAS_16)
8753 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order)
8756 return c89atomic_compare_and_swap_16((c89atomic_uint16*)ptr, 0, 0);
8759 #if defined(C89ATOMIC_HAS_32)
8760 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order)
8763 return c89atomic_compare_and_swap_32((c89atomic_uint32*)ptr, 0, 0);
8766 #if defined(C89ATOMIC_HAS_64)
8767 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order)
8770 return c89atomic_compare_and_swap_64((c89atomic_uint64*)ptr, 0, 0);
8773 #if defined(C89ATOMIC_HAS_8)
8774 #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order)
8776 #if defined(C89ATOMIC_HAS_16)
8777 #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order)
8779 #if defined(C89ATOMIC_HAS_32)
8780 #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order)
8782 #if defined(C89ATOMIC_HAS_64)
8783 #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order)
8785 #if defined(C89ATOMIC_HAS_8)
8786 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
8788 c89atomic_uint8 oldValue;
8789 c89atomic_uint8 newValue;
8792 newValue = (c89atomic_uint8)(oldValue - src);
8793 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
8798 #if defined(C89ATOMIC_HAS_16)
8799 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
8801 c89atomic_uint16 oldValue;
8802 c89atomic_uint16 newValue;
8805 newValue = (c89atomic_uint16)(oldValue - src);
8806 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
8811 #if defined(C89ATOMIC_HAS_32)
8812 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
8814 c89atomic_uint32 oldValue;
8815 c89atomic_uint32 newValue;
8818 newValue = oldValue - src;
8819 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
8824 #if defined(C89ATOMIC_HAS_64)
8825 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
8827 c89atomic_uint64 oldValue;
8828 c89atomic_uint64 newValue;
8831 newValue = oldValue - src;
8832 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
8837 #if defined(C89ATOMIC_HAS_8)
8838 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
8840 c89atomic_uint8 oldValue;
8841 c89atomic_uint8 newValue;
8844 newValue = (c89atomic_uint8)(oldValue & src);
8845 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
8850 #if defined(C89ATOMIC_HAS_16)
8851 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
8853 c89atomic_uint16 oldValue;
8854 c89atomic_uint16 newValue;
8857 newValue = (c89atomic_uint16)(oldValue & src);
8858 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
8863 #if defined(C89ATOMIC_HAS_32)
8864 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
8866 c89atomic_uint32 oldValue;
8867 c89atomic_uint32 newValue;
8870 newValue = oldValue & src;
8871 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
8876 #if defined(C89ATOMIC_HAS_64)
8877 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
8879 c89atomic_uint64 oldValue;
8880 c89atomic_uint64 newValue;
8883 newValue = oldValue & src;
8884 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
8889 #if defined(C89ATOMIC_HAS_8)
8890 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
8892 c89atomic_uint8 oldValue;
8893 c89atomic_uint8 newValue;
8896 newValue = (c89atomic_uint8)(oldValue ^ src);
8897 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
8902 #if defined(C89ATOMIC_HAS_16)
8903 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
8905 c89atomic_uint16 oldValue;
8906 c89atomic_uint16 newValue;
8909 newValue = (c89atomic_uint16)(oldValue ^ src);
8910 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
8915 #if defined(C89ATOMIC_HAS_32)
8916 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
8918 c89atomic_uint32 oldValue;
8919 c89atomic_uint32 newValue;
8922 newValue = oldValue ^ src;
8923 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
8928 #if defined(C89ATOMIC_HAS_64)
8929 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
8931 c89atomic_uint64 oldValue;
8932 c89atomic_uint64 newValue;
8935 newValue = oldValue ^ src;
8936 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
8941 #if defined(C89ATOMIC_HAS_8)
8942 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
8944 c89atomic_uint8 oldValue;
8945 c89atomic_uint8 newValue;
8948 newValue = (c89atomic_uint8)(oldValue | src);
8949 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
8954 #if defined(C89ATOMIC_HAS_16)
8955 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
8957 c89atomic_uint16 oldValue;
8958 c89atomic_uint16 newValue;
8961 newValue = (c89atomic_uint16)(oldValue | src);
8962 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
8967 #if defined(C89ATOMIC_HAS_32)
8968 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
8970 c89atomic_uint32 oldValue;
8971 c89atomic_uint32 newValue;
8974 newValue = oldValue | src;
8975 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
8980 #if defined(C89ATOMIC_HAS_64)
8981 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
8983 c89atomic_uint64 oldValue;
8984 c89atomic_uint64 newValue;
8987 newValue = oldValue | src;
8988 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
8993 #if defined(C89ATOMIC_HAS_8)
8994 #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order)
8996 #if defined(C89ATOMIC_HAS_16)
8997 #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order)
8999 #if defined(C89ATOMIC_HAS_32)
9000 #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order)
9002 #if defined(C89ATOMIC_HAS_64)
9003 #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order)
9005 #if defined(C89ATOMIC_HAS_8)
9006 #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order)
9008 #if defined(C89ATOMIC_HAS_16)
9009 #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order)
9011 #if defined(C89ATOMIC_HAS_32)
9012 #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order)
9014 #if defined(C89ATOMIC_HAS_64)
9015 #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order)
9017 #if defined(C89ATOMIC_HAS_8)
9018 typedef c89atomic_uint8 c89atomic_flag;
9019 #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order)
9020 #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order)
9021 #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order)
9023 typedef c89atomic_uint32 c89atomic_flag;
9024 #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_32(ptr, order)
9025 #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_32(ptr, order)
9026 #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_32(ptr, order)
9028 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
9029 #define C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE
9030 #define C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE
9031 #define c89atomic_memory_order_relaxed __ATOMIC_RELAXED
9032 #define c89atomic_memory_order_consume __ATOMIC_CONSUME
9033 #define c89atomic_memory_order_acquire __ATOMIC_ACQUIRE
9034 #define c89atomic_memory_order_release __ATOMIC_RELEASE
9035 #define c89atomic_memory_order_acq_rel __ATOMIC_ACQ_REL
9036 #define c89atomic_memory_order_seq_cst __ATOMIC_SEQ_CST
9037 #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory")
9038 #define c89atomic_thread_fence(order) __atomic_thread_fence(order)
9039 #define c89atomic_signal_fence(order) __atomic_signal_fence(order)
9040 #define c89atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr)
9041 #define c89atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr)
9042 #define c89atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr)
9043 #define c89atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr)
9044 #define c89atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order)
9045 #define c89atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order)
9046 #define c89atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order)
9047 #define c89atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order)
9048 #define c89atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order)
9049 #define c89atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order)
9050 #define c89atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order)
9051 #define c89atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order)
9052 #define c89atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order)
9053 #define c89atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order)
9054 #define c89atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order)
9055 #define c89atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order)
9056 #define c89atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order)
9057 #define c89atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order)
9058 #define c89atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order)
9059 #define c89atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order)
9060 #define c89atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order)
9061 #define c89atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order)
9062 #define c89atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order)
9063 #define c89atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order)
9064 #define c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
9065 #define c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
9066 #define c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
9067 #define c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
9068 #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
9069 #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
9070 #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
9071 #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
9072 #define c89atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order)
9073 #define c89atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order)
9074 #define c89atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order)
9075 #define c89atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order)
9076 #define c89atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order)
9077 #define c89atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order)
9078 #define c89atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order)
9079 #define c89atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order)
9080 #define c89atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order)
9081 #define c89atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order)
9082 #define c89atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order)
9083 #define c89atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order)
9084 #define c89atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order)
9085 #define c89atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order)
9086 #define c89atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order)
9087 #define c89atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order)
9088 #define c89atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order)
9089 #define c89atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order)
9090 #define c89atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order)
9091 #define c89atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order)
9092 #define c89atomic_compare_and_swap_8 (dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
9093 #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
9094 #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
9095 #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
9096 typedef c89atomic_uint8 c89atomic_flag;
9097 #define c89atomic_flag_test_and_set_explicit(dst, order) (c89atomic_bool)__atomic_test_and_set(dst, order)
9098 #define c89atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order)
9099 #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order)
9101 #define c89atomic_memory_order_relaxed 1
9102 #define c89atomic_memory_order_consume 2
9103 #define c89atomic_memory_order_acquire 3
9104 #define c89atomic_memory_order_release 4
9105 #define c89atomic_memory_order_acq_rel 5
9106 #define c89atomic_memory_order_seq_cst 6
9107 #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory")
9108 #if defined(__GNUC__)
9109 #define c89atomic_thread_fence(order) __sync_synchronize(), (void)order
9110 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9112 if (order > c89atomic_memory_order_acquire) {
9113 __sync_synchronize();
9115 return __sync_lock_test_and_set(dst, src);
9117 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9119 c89atomic_uint16 oldValue;
9122 } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);
9126 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9128 c89atomic_uint32 oldValue;
9131 } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);
9135 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9137 c89atomic_uint64 oldValue;
9140 } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);
9144 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9147 return __sync_fetch_and_add(dst, src);
9149 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9152 return __sync_fetch_and_add(dst, src);
9154 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9157 return __sync_fetch_and_add(dst, src);
9159 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9162 return __sync_fetch_and_add(dst, src);
9164 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9167 return __sync_fetch_and_sub(dst, src);
9169 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9172 return __sync_fetch_and_sub(dst, src);
9174 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9177 return __sync_fetch_and_sub(dst, src);
9179 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9182 return __sync_fetch_and_sub(dst, src);
9184 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9187 return __sync_fetch_and_or(dst, src);
9189 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9192 return __sync_fetch_and_or(dst, src);
9194 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9197 return __sync_fetch_and_or(dst, src);
9199 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9202 return __sync_fetch_and_or(dst, src);
9204 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9207 return __sync_fetch_and_xor(dst, src);
9209 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9212 return __sync_fetch_and_xor(dst, src);
9214 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9217 return __sync_fetch_and_xor(dst, src);
9219 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9222 return __sync_fetch_and_xor(dst, src);
9224 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9227 return __sync_fetch_and_and(dst, src);
9229 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9232 return __sync_fetch_and_and(dst, src);
9234 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9237 return __sync_fetch_and_and(dst, src);
9239 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9242 return __sync_fetch_and_and(dst, src);
9244 #define c89atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
9245 #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
9246 #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
9247 #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
9249 #if defined(C89ATOMIC_X86)
9250 #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc")
9251 #elif defined(C89ATOMIC_X64)
9252 #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc")
9254 #error Unsupported architecture. Please submit a feature request.
9256 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired)
9258 c89atomic_uint8 result;
9259 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
9260 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
9262 #error Unsupported architecture. Please submit a feature request.
9266 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired)
9268 c89atomic_uint16 result;
9269 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
9270 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
9272 #error Unsupported architecture. Please submit a feature request.
9276 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired)
9278 c89atomic_uint32 result;
9279 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
9280 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
9282 #error Unsupported architecture. Please submit a feature request.
9286 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired)
9288 volatile c89atomic_uint64 result;
9289 #if defined(C89ATOMIC_X86)
9290 c89atomic_uint32 resultEAX;
9291 c89atomic_uint32 resultEDX;
9292 __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");
9293 result = ((c89atomic_uint64)resultEDX << 32) | resultEAX;
9294 #elif defined(C89ATOMIC_X64)
9295 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
9297 #error Unsupported architecture. Please submit a feature request.
9301 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9303 c89atomic_uint8 result = 0;
9305 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
9306 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src));
9308 #error Unsupported architecture. Please submit a feature request.
9312 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9314 c89atomic_uint16 result = 0;
9316 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
9317 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src));
9319 #error Unsupported architecture. Please submit a feature request.
9323 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9325 c89atomic_uint32 result;
9327 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
9328 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src));
9330 #error Unsupported architecture. Please submit a feature request.
9334 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9336 c89atomic_uint64 result;
9338 #if defined(C89ATOMIC_X86)
9341 } while (c89atomic_compare_and_swap_64(dst, result, src) != result);
9342 #elif defined(C89ATOMIC_X64)
9343 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src));
9345 #error Unsupported architecture. Please submit a feature request.
9349 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9351 c89atomic_uint8 result;
9353 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
9354 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc");
9356 #error Unsupported architecture. Please submit a feature request.
9360 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9362 c89atomic_uint16 result;
9364 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
9365 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc");
9367 #error Unsupported architecture. Please submit a feature request.
9371 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9373 c89atomic_uint32 result;
9375 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
9376 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc");
9378 #error Unsupported architecture. Please submit a feature request.
9382 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9384 #if defined(C89ATOMIC_X86)
9385 c89atomic_uint64 oldValue;
9386 c89atomic_uint64 newValue;
9390 newValue = oldValue + src;
9391 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
9393 #elif defined(C89ATOMIC_X64)
9394 c89atomic_uint64 result;
9396 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc");
9400 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9402 c89atomic_uint8 oldValue;
9403 c89atomic_uint8 newValue;
9406 newValue = (c89atomic_uint8)(oldValue - src);
9407 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
9411 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9413 c89atomic_uint16 oldValue;
9414 c89atomic_uint16 newValue;
9417 newValue = (c89atomic_uint16)(oldValue - src);
9418 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
9422 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9424 c89atomic_uint32 oldValue;
9425 c89atomic_uint32 newValue;
9428 newValue = oldValue - src;
9429 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
9433 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9435 c89atomic_uint64 oldValue;
9436 c89atomic_uint64 newValue;
9439 newValue = oldValue - src;
9440 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
9444 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9446 c89atomic_uint8 oldValue;
9447 c89atomic_uint8 newValue;
9450 newValue = (c89atomic_uint8)(oldValue & src);
9451 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
9455 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9457 c89atomic_uint16 oldValue;
9458 c89atomic_uint16 newValue;
9461 newValue = (c89atomic_uint16)(oldValue & src);
9462 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
9466 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9468 c89atomic_uint32 oldValue;
9469 c89atomic_uint32 newValue;
9472 newValue = oldValue & src;
9473 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
9477 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9479 c89atomic_uint64 oldValue;
9480 c89atomic_uint64 newValue;
9483 newValue = oldValue & src;
9484 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
9488 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9490 c89atomic_uint8 oldValue;
9491 c89atomic_uint8 newValue;
9494 newValue = (c89atomic_uint8)(oldValue ^ src);
9495 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
9499 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9501 c89atomic_uint16 oldValue;
9502 c89atomic_uint16 newValue;
9505 newValue = (c89atomic_uint16)(oldValue ^ src);
9506 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
9510 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9512 c89atomic_uint32 oldValue;
9513 c89atomic_uint32 newValue;
9516 newValue = oldValue ^ src;
9517 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
9521 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9523 c89atomic_uint64 oldValue;
9524 c89atomic_uint64 newValue;
9527 newValue = oldValue ^ src;
9528 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
9532 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9534 c89atomic_uint8 oldValue;
9535 c89atomic_uint8 newValue;
9538 newValue = (c89atomic_uint8)(oldValue | src);
9539 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
9543 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9545 c89atomic_uint16 oldValue;
9546 c89atomic_uint16 newValue;
9549 newValue = (c89atomic_uint16)(oldValue | src);
9550 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
9554 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9556 c89atomic_uint32 oldValue;
9557 c89atomic_uint32 newValue;
9560 newValue = oldValue | src;
9561 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
9565 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9567 c89atomic_uint64 oldValue;
9568 c89atomic_uint64 newValue;
9571 newValue = oldValue | src;
9572 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
9577 #define c89atomic_signal_fence(order) c89atomic_thread_fence(order)
9578 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order)
9581 return c89atomic_compare_and_swap_8((c89atomic_uint8*)ptr, 0, 0);
9583 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order)
9586 return c89atomic_compare_and_swap_16((c89atomic_uint16*)ptr, 0, 0);
9588 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order)
9591 return c89atomic_compare_and_swap_32((c89atomic_uint32*)ptr, 0, 0);
9593 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order)
9596 return c89atomic_compare_and_swap_64((c89atomic_uint64*)ptr, 0, 0);
9598 #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order)
9599 #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order)
9600 #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order)
9601 #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order)
9602 #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order)
9603 #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order)
9604 #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order)
9605 #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order)
9606 #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order)
9607 #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order)
9608 #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order)
9609 #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order)
9610 typedef c89atomic_uint8 c89atomic_flag;
9611 #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order)
9612 #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order)
9613 #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order)
9615 #if !defined(C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE)
9616 #if defined(C89ATOMIC_HAS_8)
9617 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)
9619 c89atomic_uint8 expectedValue;
9620 c89atomic_uint8 result;
9623 expectedValue = c89atomic_load_explicit_8(expected, c89atomic_memory_order_seq_cst);
9624 result = c89atomic_compare_and_swap_8(dst, expectedValue, desired);
9625 if (result == expectedValue) {
9628 c89atomic_store_explicit_8(expected, result, failureOrder);
9633 #if defined(C89ATOMIC_HAS_16)
9634 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)
9636 c89atomic_uint16 expectedValue;
9637 c89atomic_uint16 result;
9640 expectedValue = c89atomic_load_explicit_16(expected, c89atomic_memory_order_seq_cst);
9641 result = c89atomic_compare_and_swap_16(dst, expectedValue, desired);
9642 if (result == expectedValue) {
9645 c89atomic_store_explicit_16(expected, result, failureOrder);
9650 #if defined(C89ATOMIC_HAS_32)
9651 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)
9653 c89atomic_uint32 expectedValue;
9654 c89atomic_uint32 result;
9657 expectedValue = c89atomic_load_explicit_32(expected, c89atomic_memory_order_seq_cst);
9658 result = c89atomic_compare_and_swap_32(dst, expectedValue, desired);
9659 if (result == expectedValue) {
9662 c89atomic_store_explicit_32(expected, result, failureOrder);
9667 #if defined(C89ATOMIC_HAS_64)
9668 c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
9670 c89atomic_uint64 expectedValue;
9671 c89atomic_uint64 result;
9674 expectedValue = c89atomic_load_explicit_64(expected, c89atomic_memory_order_seq_cst);
9675 result = c89atomic_compare_and_swap_64(dst, expectedValue, desired);
9676 if (result == expectedValue) {
9679 c89atomic_store_explicit_64(expected, result, failureOrder);
9684 #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder)
9685 #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder)
9686 #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder)
9687 #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder)
9689 #if !defined(C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE)
9690 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_8(volatile void* ptr)
9695 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_16(volatile void* ptr)
9700 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_32(volatile void* ptr)
9705 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_64(volatile void* ptr)
9708 #if defined(C89ATOMIC_64BIT)
9711 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
9719 #if defined(C89ATOMIC_64BIT)
9720 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr)
9722 return c89atomic_is_lock_free_64((volatile c89atomic_uint64*)ptr);
9724 static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order)
9726 return (void*)c89atomic_load_explicit_64((volatile c89atomic_uint64*)ptr, order);
9728 static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order)
9730 c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order);
9732 static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order)
9734 return (void*)c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order);
9736 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)
9738 return c89atomic_compare_exchange_strong_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder);
9740 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)
9742 return c89atomic_compare_exchange_weak_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder);
9744 static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired)
9746 return (void*)c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)desired);
9748 #elif defined(C89ATOMIC_32BIT)
9749 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr)
9751 return c89atomic_is_lock_free_32((volatile c89atomic_uint32*)ptr);
9753 static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order)
9755 return (void*)c89atomic_load_explicit_32((volatile c89atomic_uint32*)ptr, order);
9757 static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order)
9759 c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order);
9761 static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order)
9763 return (void*)c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order);
9765 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)
9767 return c89atomic_compare_exchange_strong_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder);
9769 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)
9771 return c89atomic_compare_exchange_weak_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder);
9773 static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired)
9775 return (void*)c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)desired);
9778 #error Unsupported architecture.
9780 #define c89atomic_flag_test_and_set(ptr) c89atomic_flag_test_and_set_explicit(ptr, c89atomic_memory_order_seq_cst)
9781 #define c89atomic_flag_clear(ptr) c89atomic_flag_clear_explicit(ptr, c89atomic_memory_order_seq_cst)
9782 #define c89atomic_store_ptr(dst, src) c89atomic_store_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst)
9783 #define c89atomic_load_ptr(ptr) c89atomic_load_explicit_ptr((volatile void**)ptr, c89atomic_memory_order_seq_cst)
9784 #define c89atomic_exchange_ptr(dst, src) c89atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst)
9785 #define c89atomic_compare_exchange_strong_ptr(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void*)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
9786 #define c89atomic_compare_exchange_weak_ptr(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void*)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
9787 #define c89atomic_test_and_set_8( ptr) c89atomic_test_and_set_explicit_8( ptr, c89atomic_memory_order_seq_cst)
9788 #define c89atomic_test_and_set_16(ptr) c89atomic_test_and_set_explicit_16(ptr, c89atomic_memory_order_seq_cst)
9789 #define c89atomic_test_and_set_32(ptr) c89atomic_test_and_set_explicit_32(ptr, c89atomic_memory_order_seq_cst)
9790 #define c89atomic_test_and_set_64(ptr) c89atomic_test_and_set_explicit_64(ptr, c89atomic_memory_order_seq_cst)
9791 #define c89atomic_clear_8( ptr) c89atomic_clear_explicit_8( ptr, c89atomic_memory_order_seq_cst)
9792 #define c89atomic_clear_16(ptr) c89atomic_clear_explicit_16(ptr, c89atomic_memory_order_seq_cst)
9793 #define c89atomic_clear_32(ptr) c89atomic_clear_explicit_32(ptr, c89atomic_memory_order_seq_cst)
9794 #define c89atomic_clear_64(ptr) c89atomic_clear_explicit_64(ptr, c89atomic_memory_order_seq_cst)
9795 #define c89atomic_store_8( dst, src) c89atomic_store_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
9796 #define c89atomic_store_16(dst, src) c89atomic_store_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
9797 #define c89atomic_store_32(dst, src) c89atomic_store_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
9798 #define c89atomic_store_64(dst, src) c89atomic_store_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
9799 #define c89atomic_load_8( ptr) c89atomic_load_explicit_8( ptr, c89atomic_memory_order_seq_cst)
9800 #define c89atomic_load_16(ptr) c89atomic_load_explicit_16(ptr, c89atomic_memory_order_seq_cst)
9801 #define c89atomic_load_32(ptr) c89atomic_load_explicit_32(ptr, c89atomic_memory_order_seq_cst)
9802 #define c89atomic_load_64(ptr) c89atomic_load_explicit_64(ptr, c89atomic_memory_order_seq_cst)
9803 #define c89atomic_exchange_8( dst, src) c89atomic_exchange_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
9804 #define c89atomic_exchange_16(dst, src) c89atomic_exchange_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
9805 #define c89atomic_exchange_32(dst, src) c89atomic_exchange_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
9806 #define c89atomic_exchange_64(dst, src) c89atomic_exchange_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
9807 #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)
9808 #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)
9809 #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)
9810 #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)
9811 #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)
9812 #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)
9813 #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)
9814 #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)
9815 #define c89atomic_fetch_add_8( dst, src) c89atomic_fetch_add_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
9816 #define c89atomic_fetch_add_16(dst, src) c89atomic_fetch_add_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
9817 #define c89atomic_fetch_add_32(dst, src) c89atomic_fetch_add_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
9818 #define c89atomic_fetch_add_64(dst, src) c89atomic_fetch_add_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
9819 #define c89atomic_fetch_sub_8( dst, src) c89atomic_fetch_sub_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
9820 #define c89atomic_fetch_sub_16(dst, src) c89atomic_fetch_sub_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
9821 #define c89atomic_fetch_sub_32(dst, src) c89atomic_fetch_sub_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
9822 #define c89atomic_fetch_sub_64(dst, src) c89atomic_fetch_sub_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
9823 #define c89atomic_fetch_or_8( dst, src) c89atomic_fetch_or_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
9824 #define c89atomic_fetch_or_16(dst, src) c89atomic_fetch_or_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
9825 #define c89atomic_fetch_or_32(dst, src) c89atomic_fetch_or_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
9826 #define c89atomic_fetch_or_64(dst, src) c89atomic_fetch_or_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
9827 #define c89atomic_fetch_xor_8( dst, src) c89atomic_fetch_xor_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
9828 #define c89atomic_fetch_xor_16(dst, src) c89atomic_fetch_xor_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
9829 #define c89atomic_fetch_xor_32(dst, src) c89atomic_fetch_xor_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
9830 #define c89atomic_fetch_xor_64(dst, src) c89atomic_fetch_xor_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
9831 #define c89atomic_fetch_and_8( dst, src) c89atomic_fetch_and_explicit_8 (dst, src, c89atomic_memory_order_seq_cst)
9832 #define c89atomic_fetch_and_16(dst, src) c89atomic_fetch_and_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
9833 #define c89atomic_fetch_and_32(dst, src) c89atomic_fetch_and_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
9834 #define c89atomic_fetch_and_64(dst, src) c89atomic_fetch_and_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
9835 #define c89atomic_test_and_set_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_test_and_set_explicit_8( (c89atomic_uint8* )ptr, order)
9836 #define c89atomic_test_and_set_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_test_and_set_explicit_16((c89atomic_uint16*)ptr, order)
9837 #define c89atomic_test_and_set_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_test_and_set_explicit_32((c89atomic_uint32*)ptr, order)
9838 #define c89atomic_test_and_set_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_test_and_set_explicit_64((c89atomic_uint64*)ptr, order)
9839 #define c89atomic_clear_explicit_i8( ptr, order) c89atomic_clear_explicit_8( (c89atomic_uint8* )ptr, order)
9840 #define c89atomic_clear_explicit_i16(ptr, order) c89atomic_clear_explicit_16((c89atomic_uint16*)ptr, order)
9841 #define c89atomic_clear_explicit_i32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order)
9842 #define c89atomic_clear_explicit_i64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order)
9843 #define c89atomic_store_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_store_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
9844 #define c89atomic_store_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_store_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
9845 #define c89atomic_store_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_store_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
9846 #define c89atomic_store_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_store_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
9847 #define c89atomic_load_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_load_explicit_8( (c89atomic_uint8* )ptr, order)
9848 #define c89atomic_load_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_load_explicit_16((c89atomic_uint16*)ptr, order)
9849 #define c89atomic_load_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_load_explicit_32((c89atomic_uint32*)ptr, order)
9850 #define c89atomic_load_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_load_explicit_64((c89atomic_uint64*)ptr, order)
9851 #define c89atomic_exchange_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_exchange_explicit_8 ((c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
9852 #define c89atomic_exchange_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_exchange_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
9853 #define c89atomic_exchange_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_exchange_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
9854 #define c89atomic_exchange_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_exchange_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
9855 #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)
9856 #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)
9857 #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)
9858 #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)
9859 #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)
9860 #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)
9861 #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)
9862 #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)
9863 #define c89atomic_fetch_add_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_add_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
9864 #define c89atomic_fetch_add_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_add_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
9865 #define c89atomic_fetch_add_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_add_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
9866 #define c89atomic_fetch_add_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_add_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
9867 #define c89atomic_fetch_sub_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_sub_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
9868 #define c89atomic_fetch_sub_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_sub_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
9869 #define c89atomic_fetch_sub_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_sub_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
9870 #define c89atomic_fetch_sub_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_sub_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
9871 #define c89atomic_fetch_or_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_or_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
9872 #define c89atomic_fetch_or_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_or_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
9873 #define c89atomic_fetch_or_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_or_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
9874 #define c89atomic_fetch_or_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_or_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
9875 #define c89atomic_fetch_xor_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_xor_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
9876 #define c89atomic_fetch_xor_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_xor_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
9877 #define c89atomic_fetch_xor_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_xor_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
9878 #define c89atomic_fetch_xor_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_xor_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
9879 #define c89atomic_fetch_and_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_and_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
9880 #define c89atomic_fetch_and_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_and_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
9881 #define c89atomic_fetch_and_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_and_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
9882 #define c89atomic_fetch_and_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_and_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
9883 #define c89atomic_test_and_set_i8( ptr) c89atomic_test_and_set_explicit_i8( ptr, c89atomic_memory_order_seq_cst)
9884 #define c89atomic_test_and_set_i16(ptr) c89atomic_test_and_set_explicit_i16(ptr, c89atomic_memory_order_seq_cst)
9885 #define c89atomic_test_and_set_i32(ptr) c89atomic_test_and_set_explicit_i32(ptr, c89atomic_memory_order_seq_cst)
9886 #define c89atomic_test_and_set_i64(ptr) c89atomic_test_and_set_explicit_i64(ptr, c89atomic_memory_order_seq_cst)
9887 #define c89atomic_clear_i8( ptr) c89atomic_clear_explicit_i8( ptr, c89atomic_memory_order_seq_cst)
9888 #define c89atomic_clear_i16(ptr) c89atomic_clear_explicit_i16(ptr, c89atomic_memory_order_seq_cst)
9889 #define c89atomic_clear_i32(ptr) c89atomic_clear_explicit_i32(ptr, c89atomic_memory_order_seq_cst)
9890 #define c89atomic_clear_i64(ptr) c89atomic_clear_explicit_i64(ptr, c89atomic_memory_order_seq_cst)
9891 #define c89atomic_store_i8( dst, src) c89atomic_store_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
9892 #define c89atomic_store_i16(dst, src) c89atomic_store_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
9893 #define c89atomic_store_i32(dst, src) c89atomic_store_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
9894 #define c89atomic_store_i64(dst, src) c89atomic_store_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
9895 #define c89atomic_load_i8( ptr) c89atomic_load_explicit_i8( ptr, c89atomic_memory_order_seq_cst)
9896 #define c89atomic_load_i16(ptr) c89atomic_load_explicit_i16(ptr, c89atomic_memory_order_seq_cst)
9897 #define c89atomic_load_i32(ptr) c89atomic_load_explicit_i32(ptr, c89atomic_memory_order_seq_cst)
9898 #define c89atomic_load_i64(ptr) c89atomic_load_explicit_i64(ptr, c89atomic_memory_order_seq_cst)
9899 #define c89atomic_exchange_i8( dst, src) c89atomic_exchange_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
9900 #define c89atomic_exchange_i16(dst, src) c89atomic_exchange_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
9901 #define c89atomic_exchange_i32(dst, src) c89atomic_exchange_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
9902 #define c89atomic_exchange_i64(dst, src) c89atomic_exchange_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
9903 #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)
9904 #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)
9905 #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)
9906 #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)
9907 #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)
9908 #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)
9909 #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)
9910 #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)
9911 #define c89atomic_fetch_add_i8( dst, src) c89atomic_fetch_add_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
9912 #define c89atomic_fetch_add_i16(dst, src) c89atomic_fetch_add_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
9913 #define c89atomic_fetch_add_i32(dst, src) c89atomic_fetch_add_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
9914 #define c89atomic_fetch_add_i64(dst, src) c89atomic_fetch_add_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
9915 #define c89atomic_fetch_sub_i8( dst, src) c89atomic_fetch_sub_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
9916 #define c89atomic_fetch_sub_i16(dst, src) c89atomic_fetch_sub_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
9917 #define c89atomic_fetch_sub_i32(dst, src) c89atomic_fetch_sub_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
9918 #define c89atomic_fetch_sub_i64(dst, src) c89atomic_fetch_sub_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
9919 #define c89atomic_fetch_or_i8( dst, src) c89atomic_fetch_or_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
9920 #define c89atomic_fetch_or_i16(dst, src) c89atomic_fetch_or_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
9921 #define c89atomic_fetch_or_i32(dst, src) c89atomic_fetch_or_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
9922 #define c89atomic_fetch_or_i64(dst, src) c89atomic_fetch_or_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
9923 #define c89atomic_fetch_xor_i8( dst, src) c89atomic_fetch_xor_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
9924 #define c89atomic_fetch_xor_i16(dst, src) c89atomic_fetch_xor_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
9925 #define c89atomic_fetch_xor_i32(dst, src) c89atomic_fetch_xor_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
9926 #define c89atomic_fetch_xor_i64(dst, src) c89atomic_fetch_xor_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
9927 #define c89atomic_fetch_and_i8( dst, src) c89atomic_fetch_and_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
9928 #define c89atomic_fetch_and_i16(dst, src) c89atomic_fetch_and_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
9929 #define c89atomic_fetch_and_i32(dst, src) c89atomic_fetch_and_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
9930 #define c89atomic_fetch_and_i64(dst, src) c89atomic_fetch_and_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
9941 #define c89atomic_clear_explicit_f32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order)
9942 #define c89atomic_clear_explicit_f64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order)
9943 static C89ATOMIC_INLINE void c89atomic_store_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order)
9947 c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, x.i, order);
9949 static C89ATOMIC_INLINE void c89atomic_store_explicit_f64(volatile float* dst, float src, c89atomic_memory_order order)
9953 c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, x.i, order);
9955 static C89ATOMIC_INLINE float c89atomic_load_explicit_f32(volatile float* ptr, c89atomic_memory_order order)
9958 r.i = c89atomic_load_explicit_32((volatile c89atomic_uint32*)ptr, order);
9961 static C89ATOMIC_INLINE double c89atomic_load_explicit_f64(volatile double* ptr, c89atomic_memory_order order)
9964 r.i = c89atomic_load_explicit_64((volatile c89atomic_uint64*)ptr, order);
9967 static C89ATOMIC_INLINE float c89atomic_exchange_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order)
9972 r.i = c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, x.i, order);
9975 static C89ATOMIC_INLINE double c89atomic_exchange_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order)
9980 r.i = c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, x.i, order);
9983 #define c89atomic_clear_f32(ptr) (float )c89atomic_clear_explicit_f32(ptr, c89atomic_memory_order_seq_cst)
9984 #define c89atomic_clear_f64(ptr) (double)c89atomic_clear_explicit_f64(ptr, c89atomic_memory_order_seq_cst)
9985 #define c89atomic_store_f32(dst, src) c89atomic_store_explicit_f32(dst, src, c89atomic_memory_order_seq_cst)
9986 #define c89atomic_store_f64(dst, src) c89atomic_store_explicit_f64(dst, src, c89atomic_memory_order_seq_cst)
9987 #define c89atomic_load_f32(ptr) (float )c89atomic_load_explicit_f32(ptr, c89atomic_memory_order_seq_cst)
9988 #define c89atomic_load_f64(ptr) (double)c89atomic_load_explicit_f64(ptr, c89atomic_memory_order_seq_cst)
9989 #define c89atomic_exchange_f32(dst, src) (float )c89atomic_exchange_explicit_f32(dst, src, c89atomic_memory_order_seq_cst)
9990 #define c89atomic_exchange_f64(dst, src) (double)c89atomic_exchange_explicit_f64(dst, src, c89atomic_memory_order_seq_cst)
9991 typedef c89atomic_flag c89atomic_spinlock;
9992 static C89ATOMIC_INLINE void c89atomic_spinlock_lock(volatile c89atomic_spinlock* pSpinlock)
9995 if (c89atomic_flag_test_and_set_explicit(pSpinlock, c89atomic_memory_order_acquire) == 0) {
9998 while (c89atoimc_flag_load_explicit(pSpinlock, c89atomic_memory_order_relaxed) == 1) {
10002 static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlock* pSpinlock)
10004 c89atomic_flag_clear_explicit(pSpinlock, c89atomic_memory_order_release);
10006 #if defined(__cplusplus)
10010 /* c89atomic.h end */
10014 static void* ma__malloc_default(size_t sz, void* pUserData)
10017 return MA_MALLOC(sz);
10020 static void* ma__realloc_default(void* p, size_t sz, void* pUserData)
10023 return MA_REALLOC(p, sz);
10026 static void ma__free_default(void* p, void* pUserData)
10033 static void* ma__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
10035 if (pAllocationCallbacks == NULL) {
10039 if (pAllocationCallbacks->onMalloc != NULL) {
10040 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
10043 /* Try using realloc(). */
10044 if (pAllocationCallbacks->onRealloc != NULL) {
10045 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
10051 static void* ma__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks)
10053 if (pAllocationCallbacks == NULL) {
10057 if (pAllocationCallbacks->onRealloc != NULL) {
10058 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
10061 /* Try emulating realloc() in terms of malloc()/free(). */
10062 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
10065 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
10071 MA_COPY_MEMORY(p2, p, szOld);
10072 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
10081 static MA_INLINE void* ma__calloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
10083 void* p = ma__malloc_from_callbacks(sz, pAllocationCallbacks);
10085 MA_ZERO_MEMORY(p, sz);
10091 static void ma__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
10093 if (p == NULL || pAllocationCallbacks == NULL) {
10097 if (pAllocationCallbacks->onFree != NULL) {
10098 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
10102 static ma_allocation_callbacks ma_allocation_callbacks_init_default(void)
10104 ma_allocation_callbacks callbacks;
10105 callbacks.pUserData = NULL;
10106 callbacks.onMalloc = ma__malloc_default;
10107 callbacks.onRealloc = ma__realloc_default;
10108 callbacks.onFree = ma__free_default;
10113 static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc)
10115 if (pDst == NULL) {
10116 return MA_INVALID_ARGS;
10119 if (pSrc == NULL) {
10120 *pDst = ma_allocation_callbacks_init_default();
10122 if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) {
10123 *pDst = ma_allocation_callbacks_init_default();
10125 if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) {
10126 return MA_INVALID_ARGS; /* Invalid allocation callbacks. */
10137 MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn)
10139 /* For robustness we're going to use a resampler object to calculate this since that already has a way of calculating this. */
10141 ma_uint64 frameCountOut;
10142 ma_resampler_config config;
10143 ma_resampler resampler;
10145 if (sampleRateOut == sampleRateIn) {
10146 return frameCountIn;
10149 config = ma_resampler_config_init(ma_format_s16, 1, sampleRateIn, sampleRateOut, ma_resample_algorithm_linear);
10150 result = ma_resampler_init(&config, &resampler);
10151 if (result != MA_SUCCESS) {
10155 frameCountOut = ma_resampler_get_expected_output_frame_count(&resampler, frameCountIn);
10157 ma_resampler_uninit(&resampler);
10158 return frameCountOut;
10161 #ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE
10162 #define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096
10167 #if defined(MA_WIN32)
10168 static ma_result ma_result_from_GetLastError(DWORD error)
10172 case ERROR_SUCCESS: return MA_SUCCESS;
10173 case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST;
10174 case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES;
10175 case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY;
10176 case ERROR_DISK_FULL: return MA_NO_SPACE;
10177 case ERROR_HANDLE_EOF: return MA_END_OF_FILE;
10178 case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK;
10179 case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS;
10180 case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED;
10181 case ERROR_SEM_TIMEOUT: return MA_TIMEOUT;
10182 case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST;
10188 #endif /* MA_WIN32 */
10191 /*******************************************************************************
10195 *******************************************************************************/
10196 #ifndef MA_NO_THREADING
10198 #define MA_THREADCALL WINAPI
10199 typedef unsigned long ma_thread_result;
10201 #define MA_THREADCALL
10202 typedef void* ma_thread_result;
10204 typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData);
10206 static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield)
10208 if (pSpinlock == NULL) {
10209 return MA_INVALID_ARGS;
10213 if (c89atomic_exchange_explicit_32(pSpinlock, 1, c89atomic_memory_order_acquire) == 0) {
10217 while (c89atomic_load_explicit_32(pSpinlock, c89atomic_memory_order_relaxed) == 1) {
10227 MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock)
10229 return ma_spinlock_lock_ex(pSpinlock, MA_TRUE);
10232 MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock)
10234 return ma_spinlock_lock_ex(pSpinlock, MA_FALSE);
10237 MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock)
10239 if (pSpinlock == NULL) {
10240 return MA_INVALID_ARGS;
10243 c89atomic_store_explicit_32(pSpinlock, 0, c89atomic_memory_order_release);
10248 static int ma_thread_priority_to_win32(ma_thread_priority priority)
10250 switch (priority) {
10251 case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE;
10252 case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST;
10253 case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL;
10254 case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL;
10255 case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL;
10256 case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST;
10257 case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL;
10258 default: return THREAD_PRIORITY_NORMAL;
10262 static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)
10264 *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, NULL);
10265 if (*pThread == NULL) {
10266 return ma_result_from_GetLastError(GetLastError());
10269 SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority));
10274 static void ma_thread_wait__win32(ma_thread* pThread)
10276 WaitForSingleObject((HANDLE)*pThread, INFINITE);
10277 CloseHandle((HANDLE)*pThread);
10281 static ma_result ma_mutex_init__win32(ma_mutex* pMutex)
10283 *pMutex = CreateEventW(NULL, FALSE, TRUE, NULL);
10284 if (*pMutex == NULL) {
10285 return ma_result_from_GetLastError(GetLastError());
10291 static void ma_mutex_uninit__win32(ma_mutex* pMutex)
10293 CloseHandle((HANDLE)*pMutex);
10296 static void ma_mutex_lock__win32(ma_mutex* pMutex)
10298 WaitForSingleObject((HANDLE)*pMutex, INFINITE);
10301 static void ma_mutex_unlock__win32(ma_mutex* pMutex)
10303 SetEvent((HANDLE)*pMutex);
10307 static ma_result ma_event_init__win32(ma_event* pEvent)
10309 *pEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
10310 if (*pEvent == NULL) {
10311 return ma_result_from_GetLastError(GetLastError());
10317 static void ma_event_uninit__win32(ma_event* pEvent)
10319 CloseHandle((HANDLE)*pEvent);
10322 static ma_result ma_event_wait__win32(ma_event* pEvent)
10324 DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE);
10325 if (result == WAIT_OBJECT_0) {
10329 if (result == WAIT_TIMEOUT) {
10333 return ma_result_from_GetLastError(GetLastError());
10336 static ma_result ma_event_signal__win32(ma_event* pEvent)
10338 BOOL result = SetEvent((HANDLE)*pEvent);
10340 return ma_result_from_GetLastError(GetLastError());
10347 static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore)
10349 *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL);
10350 if (*pSemaphore == NULL) {
10351 return ma_result_from_GetLastError(GetLastError());
10357 static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore)
10359 CloseHandle((HANDLE)*pSemaphore);
10362 static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore)
10364 DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE);
10365 if (result == WAIT_OBJECT_0) {
10369 if (result == WAIT_TIMEOUT) {
10373 return ma_result_from_GetLastError(GetLastError());
10376 static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore)
10378 BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL);
10380 return ma_result_from_GetLastError(GetLastError());
10389 static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)
10392 pthread_attr_t* pAttr = NULL;
10394 #if !defined(__EMSCRIPTEN__)
10395 /* Try setting the thread priority. It's not critical if anything fails here. */
10396 pthread_attr_t attr;
10397 if (pthread_attr_init(&attr) == 0) {
10398 int scheduler = -1;
10399 if (priority == ma_thread_priority_idle) {
10401 if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) {
10402 scheduler = SCHED_IDLE;
10405 } else if (priority == ma_thread_priority_realtime) {
10407 if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) {
10408 scheduler = SCHED_FIFO;
10413 scheduler = sched_getscheduler(0);
10417 if (stackSize > 0) {
10418 pthread_attr_setstacksize(&attr, stackSize);
10421 if (scheduler != -1) {
10422 int priorityMin = sched_get_priority_min(scheduler);
10423 int priorityMax = sched_get_priority_max(scheduler);
10424 int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */
10426 struct sched_param sched;
10427 if (pthread_attr_getschedparam(&attr, &sched) == 0) {
10428 if (priority == ma_thread_priority_idle) {
10429 sched.sched_priority = priorityMin;
10430 } else if (priority == ma_thread_priority_realtime) {
10431 sched.sched_priority = priorityMax;
10433 sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */
10434 if (sched.sched_priority < priorityMin) {
10435 sched.sched_priority = priorityMin;
10437 if (sched.sched_priority > priorityMax) {
10438 sched.sched_priority = priorityMax;
10442 if (pthread_attr_setschedparam(&attr, &sched) == 0) {
10449 /* It's the emscripten build. We'll have a few unused parameters. */
10454 result = pthread_create(pThread, pAttr, entryProc, pData);
10456 /* The thread attributes object is no longer required. */
10457 if (pAttr != NULL) {
10458 pthread_attr_destroy(pAttr);
10462 return ma_result_from_errno(result);
10468 static void ma_thread_wait__posix(ma_thread* pThread)
10470 pthread_join(*pThread, NULL);
10471 pthread_detach(*pThread);
10475 static ma_result ma_mutex_init__posix(ma_mutex* pMutex)
10477 int result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL);
10479 return ma_result_from_errno(result);
10485 static void ma_mutex_uninit__posix(ma_mutex* pMutex)
10487 pthread_mutex_destroy((pthread_mutex_t*)pMutex);
10490 static void ma_mutex_lock__posix(ma_mutex* pMutex)
10492 pthread_mutex_lock((pthread_mutex_t*)pMutex);
10495 static void ma_mutex_unlock__posix(ma_mutex* pMutex)
10497 pthread_mutex_unlock((pthread_mutex_t*)pMutex);
10501 static ma_result ma_event_init__posix(ma_event* pEvent)
10505 result = pthread_mutex_init(&pEvent->lock, NULL);
10507 return ma_result_from_errno(result);
10510 result = pthread_cond_init(&pEvent->cond, NULL);
10512 pthread_mutex_destroy(&pEvent->lock);
10513 return ma_result_from_errno(result);
10520 static void ma_event_uninit__posix(ma_event* pEvent)
10522 pthread_cond_destroy(&pEvent->cond);
10523 pthread_mutex_destroy(&pEvent->lock);
10526 static ma_result ma_event_wait__posix(ma_event* pEvent)
10528 pthread_mutex_lock(&pEvent->lock);
10530 while (pEvent->value == 0) {
10531 pthread_cond_wait(&pEvent->cond, &pEvent->lock);
10533 pEvent->value = 0; /* Auto-reset. */
10535 pthread_mutex_unlock(&pEvent->lock);
10540 static ma_result ma_event_signal__posix(ma_event* pEvent)
10542 pthread_mutex_lock(&pEvent->lock);
10545 pthread_cond_signal(&pEvent->cond);
10547 pthread_mutex_unlock(&pEvent->lock);
10553 static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemaphore)
10557 if (pSemaphore == NULL) {
10558 return MA_INVALID_ARGS;
10561 pSemaphore->value = initialValue;
10563 result = pthread_mutex_init(&pSemaphore->lock, NULL);
10565 return ma_result_from_errno(result); /* Failed to create mutex. */
10568 result = pthread_cond_init(&pSemaphore->cond, NULL);
10570 pthread_mutex_destroy(&pSemaphore->lock);
10571 return ma_result_from_errno(result); /* Failed to create condition variable. */
10577 static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore)
10579 if (pSemaphore == NULL) {
10583 pthread_cond_destroy(&pSemaphore->cond);
10584 pthread_mutex_destroy(&pSemaphore->lock);
10587 static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore)
10589 if (pSemaphore == NULL) {
10590 return MA_INVALID_ARGS;
10593 pthread_mutex_lock(&pSemaphore->lock);
10595 /* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */
10596 while (pSemaphore->value == 0) {
10597 pthread_cond_wait(&pSemaphore->cond, &pSemaphore->lock);
10600 pSemaphore->value -= 1;
10602 pthread_mutex_unlock(&pSemaphore->lock);
10607 static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore)
10609 if (pSemaphore == NULL) {
10610 return MA_INVALID_ARGS;
10613 pthread_mutex_lock(&pSemaphore->lock);
10615 pSemaphore->value += 1;
10616 pthread_cond_signal(&pSemaphore->cond);
10618 pthread_mutex_unlock(&pSemaphore->lock);
10624 static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)
10626 if (pThread == NULL || entryProc == NULL) {
10631 return ma_thread_create__win32(pThread, priority, stackSize, entryProc, pData);
10634 return ma_thread_create__posix(pThread, priority, stackSize, entryProc, pData);
10638 static void ma_thread_wait(ma_thread* pThread)
10640 if (pThread == NULL) {
10645 ma_thread_wait__win32(pThread);
10648 ma_thread_wait__posix(pThread);
10653 MA_API ma_result ma_mutex_init(ma_mutex* pMutex)
10655 if (pMutex == NULL) {
10656 MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */
10657 return MA_INVALID_ARGS;
10661 return ma_mutex_init__win32(pMutex);
10664 return ma_mutex_init__posix(pMutex);
10668 MA_API void ma_mutex_uninit(ma_mutex* pMutex)
10670 if (pMutex == NULL) {
10675 ma_mutex_uninit__win32(pMutex);
10678 ma_mutex_uninit__posix(pMutex);
10682 MA_API void ma_mutex_lock(ma_mutex* pMutex)
10684 if (pMutex == NULL) {
10685 MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */
10690 ma_mutex_lock__win32(pMutex);
10693 ma_mutex_lock__posix(pMutex);
10697 MA_API void ma_mutex_unlock(ma_mutex* pMutex)
10699 if (pMutex == NULL) {
10700 MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */
10705 ma_mutex_unlock__win32(pMutex);
10708 ma_mutex_unlock__posix(pMutex);
10713 MA_API ma_result ma_event_init(ma_event* pEvent)
10715 if (pEvent == NULL) {
10716 MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */
10717 return MA_INVALID_ARGS;
10721 return ma_event_init__win32(pEvent);
10724 return ma_event_init__posix(pEvent);
10729 static ma_result ma_event_alloc_and_init(ma_event** ppEvent, ma_allocation_callbacks* pAllocationCallbacks)
10734 if (ppEvent == NULL) {
10735 return MA_INVALID_ARGS;
10740 pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks/*, MA_ALLOCATION_TYPE_EVENT*/);
10741 if (pEvent == NULL) {
10742 return MA_OUT_OF_MEMORY;
10745 result = ma_event_init(pEvent);
10746 if (result != MA_SUCCESS) {
10747 ma_free(pEvent, pAllocationCallbacks/*, MA_ALLOCATION_TYPE_EVENT*/);
10756 MA_API void ma_event_uninit(ma_event* pEvent)
10758 if (pEvent == NULL) {
10763 ma_event_uninit__win32(pEvent);
10766 ma_event_uninit__posix(pEvent);
10771 static void ma_event_uninit_and_free(ma_event* pEvent, ma_allocation_callbacks* pAllocationCallbacks)
10773 if (pEvent == NULL) {
10777 ma_event_uninit(pEvent);
10778 ma_free(pEvent, pAllocationCallbacks/*, MA_ALLOCATION_TYPE_EVENT*/);
10782 MA_API ma_result ma_event_wait(ma_event* pEvent)
10784 if (pEvent == NULL) {
10785 MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */
10786 return MA_INVALID_ARGS;
10790 return ma_event_wait__win32(pEvent);
10793 return ma_event_wait__posix(pEvent);
10797 MA_API ma_result ma_event_signal(ma_event* pEvent)
10799 if (pEvent == NULL) {
10800 MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */
10801 return MA_INVALID_ARGS;
10805 return ma_event_signal__win32(pEvent);
10808 return ma_event_signal__posix(pEvent);
10813 MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore)
10815 if (pSemaphore == NULL) {
10816 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
10817 return MA_INVALID_ARGS;
10821 return ma_semaphore_init__win32(initialValue, pSemaphore);
10824 return ma_semaphore_init__posix(initialValue, pSemaphore);
10828 MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore)
10830 if (pSemaphore == NULL) {
10831 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
10836 ma_semaphore_uninit__win32(pSemaphore);
10839 ma_semaphore_uninit__posix(pSemaphore);
10843 MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore)
10845 if (pSemaphore == NULL) {
10846 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
10847 return MA_INVALID_ARGS;
10851 return ma_semaphore_wait__win32(pSemaphore);
10854 return ma_semaphore_wait__posix(pSemaphore);
10858 MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore)
10860 if (pSemaphore == NULL) {
10861 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
10862 return MA_INVALID_ARGS;
10866 return ma_semaphore_release__win32(pSemaphore);
10869 return ma_semaphore_release__posix(pSemaphore);
10873 /* 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. */
10874 #ifndef MA_NO_DEVICE_IO
10875 #error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO";
10877 #endif /* MA_NO_THREADING */
10881 /************************************************************************************************************************************************************
10882 *************************************************************************************************************************************************************
10887 *************************************************************************************************************************************************************
10888 ************************************************************************************************************************************************************/
10889 #ifndef MA_NO_DEVICE_IO
10891 #include <objbase.h>
10893 #include <mmsystem.h>
10896 #if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
10897 #include <mach/mach_time.h> /* For mach_absolute_time() */
10901 #include <sys/types.h>
10902 #include <unistd.h>
10907 Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When
10908 using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing
10909 compile-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
10910 disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am
10911 not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere.
10913 /*#define MA_USE_RUNTIME_LINKING_FOR_PTHREAD*/
10915 /* Disable run-time linking on certain backends. */
10916 #ifndef MA_NO_RUNTIME_LINKING
10917 #if defined(MA_EMSCRIPTEN)
10918 #define MA_NO_RUNTIME_LINKING
10923 MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags)
10925 if (pDeviceInfo == NULL) {
10929 if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats)) {
10930 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
10931 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
10932 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
10933 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
10934 pDeviceInfo->nativeDataFormatCount += 1;
10939 MA_API const char* ma_get_backend_name(ma_backend backend)
10943 case ma_backend_wasapi: return "WASAPI";
10944 case ma_backend_dsound: return "DirectSound";
10945 case ma_backend_winmm: return "WinMM";
10946 case ma_backend_coreaudio: return "Core Audio";
10947 case ma_backend_sndio: return "sndio";
10948 case ma_backend_audio4: return "audio(4)";
10949 case ma_backend_oss: return "OSS";
10950 case ma_backend_pulseaudio: return "PulseAudio";
10951 case ma_backend_alsa: return "ALSA";
10952 case ma_backend_jack: return "JACK";
10953 case ma_backend_aaudio: return "AAudio";
10954 case ma_backend_opensl: return "OpenSL|ES";
10955 case ma_backend_webaudio: return "Web Audio";
10956 case ma_backend_custom: return "Custom";
10957 case ma_backend_null: return "Null";
10958 default: return "Unknown";
10962 MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend)
10965 This looks a little bit gross, but we want all backends to be included in the switch to avoid warnings on some compilers
10966 about some enums not being handled by the switch statement.
10970 case ma_backend_wasapi:
10971 #if defined(MA_HAS_WASAPI)
10976 case ma_backend_dsound:
10977 #if defined(MA_HAS_DSOUND)
10982 case ma_backend_winmm:
10983 #if defined(MA_HAS_WINMM)
10988 case ma_backend_coreaudio:
10989 #if defined(MA_HAS_COREAUDIO)
10994 case ma_backend_sndio:
10995 #if defined(MA_HAS_SNDIO)
11000 case ma_backend_audio4:
11001 #if defined(MA_HAS_AUDIO4)
11006 case ma_backend_oss:
11007 #if defined(MA_HAS_OSS)
11012 case ma_backend_pulseaudio:
11013 #if defined(MA_HAS_PULSEAUDIO)
11018 case ma_backend_alsa:
11019 #if defined(MA_HAS_ALSA)
11024 case ma_backend_jack:
11025 #if defined(MA_HAS_JACK)
11030 case ma_backend_aaudio:
11031 #if defined(MA_HAS_AAUDIO)
11036 case ma_backend_opensl:
11037 #if defined(MA_HAS_OPENSL)
11042 case ma_backend_webaudio:
11043 #if defined(MA_HAS_WEBAUDIO)
11048 case ma_backend_custom:
11049 #if defined(MA_HAS_CUSTOM)
11054 case ma_backend_null:
11055 #if defined(MA_HAS_NULL)
11061 default: return MA_FALSE;
11065 MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount)
11067 size_t backendCount;
11069 ma_result result = MA_SUCCESS;
11071 if (pBackendCount == NULL) {
11072 return MA_INVALID_ARGS;
11077 for (iBackend = 0; iBackend <= ma_backend_null; iBackend += 1) {
11078 ma_backend backend = (ma_backend)iBackend;
11080 if (ma_is_backend_enabled(backend)) {
11081 /* The backend is enabled. Try adding it to the list. If there's no room, MA_NO_SPACE needs to be returned. */
11082 if (backendCount == backendCap) {
11083 result = MA_NO_SPACE;
11086 pBackends[backendCount] = backend;
11092 if (pBackendCount != NULL) {
11093 *pBackendCount = backendCount;
11099 MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend)
11103 case ma_backend_wasapi: return MA_TRUE;
11104 case ma_backend_dsound: return MA_FALSE;
11105 case ma_backend_winmm: return MA_FALSE;
11106 case ma_backend_coreaudio: return MA_FALSE;
11107 case ma_backend_sndio: return MA_FALSE;
11108 case ma_backend_audio4: return MA_FALSE;
11109 case ma_backend_oss: return MA_FALSE;
11110 case ma_backend_pulseaudio: return MA_FALSE;
11111 case ma_backend_alsa: return MA_FALSE;
11112 case ma_backend_jack: return MA_FALSE;
11113 case ma_backend_aaudio: return MA_FALSE;
11114 case ma_backend_opensl: return MA_FALSE;
11115 case ma_backend_webaudio: return MA_FALSE;
11116 case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */
11117 case ma_backend_null: return MA_FALSE;
11118 default: return MA_FALSE;
11125 /* WASAPI error codes. */
11126 #define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001)
11127 #define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002)
11128 #define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003)
11129 #define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004)
11130 #define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005)
11131 #define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006)
11132 #define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007)
11133 #define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008)
11134 #define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009)
11135 #define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A)
11136 #define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B)
11137 #define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C)
11138 #define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D)
11139 #define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E)
11140 #define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F)
11141 #define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010)
11142 #define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011)
11143 #define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012)
11144 #define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013)
11145 #define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014)
11146 #define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015)
11147 #define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016)
11148 #define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017)
11149 #define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018)
11150 #define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019)
11151 #define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020)
11152 #define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021)
11153 #define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022)
11154 #define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023)
11155 #define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024)
11156 #define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025)
11157 #define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026)
11158 #define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027)
11159 #define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028)
11160 #define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029)
11161 #define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030)
11162 #define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040)
11163 #define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001)
11164 #define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002)
11165 #define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003)
11167 #define MA_DS_OK ((HRESULT)0)
11168 #define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A)
11169 #define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A)
11170 #define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E)
11171 #define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057) /*E_INVALIDARG*/
11172 #define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032)
11173 #define MA_DSERR_GENERIC ((HRESULT)0x80004005) /*E_FAIL*/
11174 #define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046)
11175 #define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/
11176 #define MA_DSERR_BADFORMAT ((HRESULT)0x88780064)
11177 #define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001) /*E_NOTIMPL*/
11178 #define MA_DSERR_NODRIVER ((HRESULT)0x88780078)
11179 #define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082)
11180 #define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/
11181 #define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096)
11182 #define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0)
11183 #define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA)
11184 #define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002) /*E_NOINTERFACE*/
11185 #define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005) /*E_ACCESSDENIED*/
11186 #define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4)
11187 #define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE)
11188 #define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8)
11189 #define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2)
11190 #define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161)
11191 #define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC)
11193 static ma_result ma_result_from_HRESULT(HRESULT hr)
11197 case NOERROR: return MA_SUCCESS;
11198 /*case S_OK: return MA_SUCCESS;*/
11200 case E_POINTER: return MA_INVALID_ARGS;
11201 case E_UNEXPECTED: return MA_ERROR;
11202 case E_NOTIMPL: return MA_NOT_IMPLEMENTED;
11203 case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY;
11204 case E_INVALIDARG: return MA_INVALID_ARGS;
11205 case E_NOINTERFACE: return MA_API_NOT_FOUND;
11206 case E_HANDLE: return MA_INVALID_ARGS;
11207 case E_ABORT: return MA_ERROR;
11208 case E_FAIL: return MA_ERROR;
11209 case E_ACCESSDENIED: return MA_ACCESS_DENIED;
11212 case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED;
11213 case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED;
11214 case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS;
11215 case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE;
11216 case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED;
11217 case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG;
11218 case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION;
11219 case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED;
11220 case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS;
11221 case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY;
11222 case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION;
11223 case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST;
11224 case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION;
11225 case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED;
11226 case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
11227 case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED;
11228 case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS;
11229 case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED;
11230 case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS;
11231 case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS;
11232 case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS;
11233 case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS;
11234 case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR;
11235 case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR;
11236 case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS;
11237 case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS;
11238 case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS;
11239 case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION;
11240 case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY;
11241 case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
11242 case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
11243 case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA;
11244 case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION;
11245 case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION;
11246 case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION;
11247 case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION;
11248 case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION;
11249 case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE;
11250 case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS;
11251 case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR;
11254 /*case MA_DS_OK: return MA_SUCCESS;*/ /* S_OK */
11255 case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS;
11256 case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE;
11257 case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION;
11258 /*case MA_DSERR_INVALIDPARAM: return MA_INVALID_ARGS;*/ /* E_INVALIDARG */
11259 case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION;
11260 /*case MA_DSERR_GENERIC: return MA_ERROR;*/ /* E_FAIL */
11261 case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION;
11262 /*case MA_DSERR_OUTOFMEMORY: return MA_OUT_OF_MEMORY;*/ /* E_OUTOFMEMORY */
11263 case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED;
11264 /*case MA_DSERR_UNSUPPORTED: return MA_NOT_IMPLEMENTED;*/ /* E_NOTIMPL */
11265 case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND;
11266 case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED;
11267 case MA_DSERR_NOAGGREGATION: return MA_ERROR;
11268 case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE;
11269 case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED;
11270 case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED;
11271 /*case MA_DSERR_NOINTERFACE: return MA_API_NOT_FOUND;*/ /* E_NOINTERFACE */
11272 /*case MA_DSERR_ACCESSDENIED: return MA_ACCESS_DENIED;*/ /* E_ACCESSDENIED */
11273 case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE;
11274 case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION;
11275 case MA_DSERR_SENDLOOP: return MA_DEADLOCK;
11276 case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS;
11277 case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE;
11278 case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE;
11280 default: return MA_ERROR;
11284 typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit);
11285 typedef void (WINAPI * MA_PFN_CoUninitialize)(void);
11286 typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv);
11287 typedef void (WINAPI * MA_PFN_CoTaskMemFree)(LPVOID pv);
11288 typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(PROPVARIANT *pvar);
11289 typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax);
11291 typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void);
11292 typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void);
11294 /* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */
11295 typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult);
11296 typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey);
11297 typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData);
11301 #define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device"
11302 #define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device"
11305 MA_API const char* ma_log_level_to_string(ma_uint32 logLevel)
11309 case MA_LOG_LEVEL_VERBOSE: return "";
11310 case MA_LOG_LEVEL_INFO: return "INFO";
11311 case MA_LOG_LEVEL_WARNING: return "WARNING";
11312 case MA_LOG_LEVEL_ERROR: return "ERROR";
11313 default: return "ERROR";
11317 /* Posts a log message. */
11318 static void ma_post_log_message(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
11320 if (pContext == NULL) {
11321 if (pDevice != NULL) {
11322 pContext = pDevice->pContext;
11326 /* All logs must be output when debug output is enabled. */
11327 #if defined(MA_DEBUG_OUTPUT)
11328 printf("%s: %s\n", ma_log_level_to_string(logLevel), message);
11331 if (pContext == NULL) {
11335 #if defined(MA_LOG_LEVEL)
11336 if (logLevel <= MA_LOG_LEVEL) {
11339 onLog = pContext->logCallback;
11341 onLog(pContext, pDevice, logLevel, message);
11348 We need to emulate _vscprintf() for the VC6 build. This can be more efficient, but since it's only VC6, and it's just a
11349 logging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf().
11351 #if defined(_MSC_VER) && _MSC_VER < 1900
11352 int ma_vscprintf(const char* format, va_list args)
11354 #if _MSC_VER > 1200
11355 return _vscprintf(format, args);
11358 char* pTempBuffer = NULL;
11359 size_t tempBufferCap = 1024;
11361 if (format == NULL) {
11367 char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, NULL); /* TODO: Add support for custom memory allocators? */
11368 if (pNewTempBuffer == NULL) {
11369 ma_free(pTempBuffer, NULL);
11371 return -1; /* Out of memory. */
11374 pTempBuffer = pNewTempBuffer;
11376 result = _vsnprintf(pTempBuffer, tempBufferCap, format, args);
11377 ma_free(pTempBuffer, NULL);
11379 if (result != -1) {
11380 break; /* Got it. */
11383 /* 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. */
11384 tempBufferCap *= 2;
11392 /* Posts a formatted log message. */
11393 static void ma_post_log_messagev(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* pFormat, va_list args)
11395 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
11397 char pFormattedMessage[1024];
11398 vsnprintf(pFormattedMessage, sizeof(pFormattedMessage), pFormat, args);
11399 ma_post_log_message(pContext, pDevice, logLevel, pFormattedMessage);
11404 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
11405 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
11406 a fixed sized stack allocated buffer.
11408 #if defined(_MSC_VER) && _MSC_VER >= 1200 /* 1200 = VC6 */
11412 #if _MSC_VER >= 1800
11413 va_copy(args2, args);
11417 formattedLen = ma_vscprintf(pFormat, args2);
11420 if (formattedLen > 0) {
11421 char* pFormattedMessage = NULL;
11422 ma_allocation_callbacks* pAllocationCallbacks = NULL;
11424 /* Make sure we have a context so we can allocate memory. */
11425 if (pContext == NULL) {
11426 if (pDevice != NULL) {
11427 pContext = pDevice->pContext;
11431 if (pContext != NULL) {
11432 pAllocationCallbacks = &pContext->allocationCallbacks;
11435 pFormattedMessage = (char*)ma_malloc(formattedLen + 1, pAllocationCallbacks);
11436 if (pFormattedMessage != NULL) {
11437 /* We'll get errors on newer versions of Visual Studio if we try to use vsprintf(). */
11438 #if _MSC_VER >= 1400 /* 1400 = Visual Studio 2005 */
11439 vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args);
11441 vsprintf(pFormattedMessage, pFormat, args);
11444 ma_post_log_message(pContext, pDevice, logLevel, pFormattedMessage);
11445 ma_free(pFormattedMessage, pAllocationCallbacks);
11449 /* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */
11460 MA_API void ma_post_log_messagef(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* pFormat, ...)
11463 va_start(args, pFormat);
11465 ma_post_log_messagev(pContext, pDevice, logLevel, pFormat, args);
11470 /* Posts an log message. Throw a breakpoint in here if you're needing to debug. The return value is always "resultCode". */
11471 static ma_result ma_context_post_error(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode)
11473 ma_post_log_message(pContext, pDevice, logLevel, message);
11477 static ma_result ma_post_error(ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode)
11479 return ma_context_post_error(NULL, pDevice, logLevel, message, resultCode);
11483 /*******************************************************************************
11487 *******************************************************************************/
11489 static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */
11490 static void ma_timer_init(ma_timer* pTimer)
11492 LARGE_INTEGER counter;
11494 if (g_ma_TimerFrequency.QuadPart == 0) {
11495 QueryPerformanceFrequency(&g_ma_TimerFrequency);
11498 QueryPerformanceCounter(&counter);
11499 pTimer->counter = counter.QuadPart;
11502 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
11504 LARGE_INTEGER counter;
11505 if (!QueryPerformanceCounter(&counter)) {
11509 return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart;
11511 #elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
11512 static ma_uint64 g_ma_TimerFrequency = 0;
11513 static void ma_timer_init(ma_timer* pTimer)
11515 mach_timebase_info_data_t baseTime;
11516 mach_timebase_info(&baseTime);
11517 g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer;
11519 pTimer->counter = mach_absolute_time();
11522 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
11524 ma_uint64 newTimeCounter = mach_absolute_time();
11525 ma_uint64 oldTimeCounter = pTimer->counter;
11527 return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency;
11529 #elif defined(MA_EMSCRIPTEN)
11530 static MA_INLINE void ma_timer_init(ma_timer* pTimer)
11532 pTimer->counterD = emscripten_get_now();
11535 static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)
11537 return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */
11540 #if _POSIX_C_SOURCE >= 199309L
11541 #if defined(CLOCK_MONOTONIC)
11542 #define MA_CLOCK_ID CLOCK_MONOTONIC
11544 #define MA_CLOCK_ID CLOCK_REALTIME
11547 static void ma_timer_init(ma_timer* pTimer)
11549 struct timespec newTime;
11550 clock_gettime(MA_CLOCK_ID, &newTime);
11552 pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
11555 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
11557 ma_uint64 newTimeCounter;
11558 ma_uint64 oldTimeCounter;
11560 struct timespec newTime;
11561 clock_gettime(MA_CLOCK_ID, &newTime);
11563 newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
11564 oldTimeCounter = pTimer->counter;
11566 return (newTimeCounter - oldTimeCounter) / 1000000000.0;
11569 static void ma_timer_init(ma_timer* pTimer)
11571 struct timeval newTime;
11572 gettimeofday(&newTime, NULL);
11574 pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
11577 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
11579 ma_uint64 newTimeCounter;
11580 ma_uint64 oldTimeCounter;
11582 struct timeval newTime;
11583 gettimeofday(&newTime, NULL);
11585 newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
11586 oldTimeCounter = pTimer->counter;
11588 return (newTimeCounter - oldTimeCounter) / 1000000.0;
11594 /*******************************************************************************
11598 *******************************************************************************/
11599 MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename)
11603 #if MA_LOG_LEVEL >= MA_LOG_LEVEL_VERBOSE
11604 if (pContext != NULL) {
11606 ma_strappend(message, sizeof(message), "Loading library: ", filename);
11607 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, message);
11612 #ifdef MA_WIN32_DESKTOP
11613 handle = (ma_handle)LoadLibraryA(filename);
11615 /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */
11616 WCHAR filenameW[4096];
11617 if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) {
11620 handle = (ma_handle)LoadPackagedLibrary(filenameW, 0);
11624 handle = (ma_handle)dlopen(filename, RTLD_NOW);
11628 I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority
11629 backend is a deliberate design choice. Instead I'm logging it as an informational message.
11631 #if MA_LOG_LEVEL >= MA_LOG_LEVEL_INFO
11632 if (handle == NULL) {
11634 ma_strappend(message, sizeof(message), "Failed to load library: ", filename);
11635 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, message);
11639 (void)pContext; /* It's possible for pContext to be unused. */
11643 MA_API void ma_dlclose(ma_context* pContext, ma_handle handle)
11646 FreeLibrary((HMODULE)handle);
11648 dlclose((void*)handle);
11654 MA_API ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symbol)
11658 #if MA_LOG_LEVEL >= MA_LOG_LEVEL_VERBOSE
11659 if (pContext != NULL) {
11661 ma_strappend(message, sizeof(message), "Loading symbol: ", symbol);
11662 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, message);
11667 proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol);
11669 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
11670 #pragma GCC diagnostic push
11671 #pragma GCC diagnostic ignored "-Wpedantic"
11673 proc = (ma_proc)dlsym((void*)handle, symbol);
11674 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
11675 #pragma GCC diagnostic pop
11679 #if MA_LOG_LEVEL >= MA_LOG_LEVEL_WARNING
11680 if (handle == NULL) {
11682 ma_strappend(message, sizeof(message), "Failed to load symbol: ", symbol);
11683 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_WARNING, message);
11687 (void)pContext; /* It's possible for pContext to be unused. */
11693 static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn)
11695 ma_uint32 closestRate = 0;
11696 ma_uint32 closestDiff = 0xFFFFFFFF;
11697 size_t iStandardRate;
11699 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
11700 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
11703 if (sampleRateIn > standardRate) {
11704 diff = sampleRateIn - standardRate;
11706 diff = standardRate - sampleRateIn;
11710 return standardRate; /* The input sample rate is a standard rate. */
11713 if (closestDiff > diff) {
11714 closestDiff = diff;
11715 closestRate = standardRate;
11719 return closestRate;
11724 static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
11726 float masterVolumeFactor;
11728 ma_device_get_master_volume(pDevice, &masterVolumeFactor); /* Use ma_device_get_master_volume() to ensure the volume is loaded atomically. */
11730 if (pDevice->onData) {
11731 if (!pDevice->noPreZeroedOutputBuffer && pFramesOut != NULL) {
11732 ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels);
11735 /* 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. */
11736 if (pFramesIn != NULL && masterVolumeFactor < 1) {
11737 ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
11738 ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
11739 ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
11740 ma_uint32 totalFramesProcessed = 0;
11741 while (totalFramesProcessed < frameCount) {
11742 ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed;
11743 if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) {
11744 framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture;
11747 ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor);
11749 pDevice->onData(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration);
11751 totalFramesProcessed += framesToProcessThisIteration;
11754 pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount);
11757 /* Volume control and clipping for playback devices. */
11758 if (pFramesOut != NULL) {
11759 if (masterVolumeFactor < 1) {
11760 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. */
11761 ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor);
11765 if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) {
11766 ma_clip_pcm_frames_f32((float*)pFramesOut, frameCount, pDevice->playback.channels);
11774 /* A helper function for reading sample data from the client. */
11775 static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut)
11777 MA_ASSERT(pDevice != NULL);
11778 MA_ASSERT(frameCount > 0);
11779 MA_ASSERT(pFramesOut != NULL);
11781 if (pDevice->playback.converter.isPassthrough) {
11782 ma_device__on_data(pDevice, pFramesOut, NULL, frameCount);
11785 ma_uint64 totalFramesReadOut;
11786 ma_uint64 totalFramesReadIn;
11787 void* pRunningFramesOut;
11789 totalFramesReadOut = 0;
11790 totalFramesReadIn = 0;
11791 pRunningFramesOut = pFramesOut;
11793 while (totalFramesReadOut < frameCount) {
11794 ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */
11795 ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
11796 ma_uint64 framesToReadThisIterationIn;
11797 ma_uint64 framesReadThisIterationIn;
11798 ma_uint64 framesToReadThisIterationOut;
11799 ma_uint64 framesReadThisIterationOut;
11800 ma_uint64 requiredInputFrameCount;
11802 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
11803 framesToReadThisIterationIn = framesToReadThisIterationOut;
11804 if (framesToReadThisIterationIn > intermediaryBufferCap) {
11805 framesToReadThisIterationIn = intermediaryBufferCap;
11808 requiredInputFrameCount = ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut);
11809 if (framesToReadThisIterationIn > requiredInputFrameCount) {
11810 framesToReadThisIterationIn = requiredInputFrameCount;
11813 if (framesToReadThisIterationIn > 0) {
11814 ma_device__on_data(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn);
11815 totalFramesReadIn += framesToReadThisIterationIn;
11819 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
11820 input frames, we still want to try processing frames because there may some output frames generated from cached input data.
11822 framesReadThisIterationIn = framesToReadThisIterationIn;
11823 framesReadThisIterationOut = framesToReadThisIterationOut;
11824 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
11825 if (result != MA_SUCCESS) {
11829 totalFramesReadOut += framesReadThisIterationOut;
11830 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
11832 if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
11833 break; /* We're done. */
11839 /* A helper for sending sample data to the client. */
11840 static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat)
11842 MA_ASSERT(pDevice != NULL);
11843 MA_ASSERT(frameCountInDeviceFormat > 0);
11844 MA_ASSERT(pFramesInDeviceFormat != NULL);
11846 if (pDevice->capture.converter.isPassthrough) {
11847 ma_device__on_data(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat);
11850 ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
11851 ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
11852 ma_uint64 totalDeviceFramesProcessed = 0;
11853 ma_uint64 totalClientFramesProcessed = 0;
11854 const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;
11856 /* We just keep going until we've exhaused all of our input frames and cannot generate any more output frames. */
11858 ma_uint64 deviceFramesProcessedThisIteration;
11859 ma_uint64 clientFramesProcessedThisIteration;
11861 deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
11862 clientFramesProcessedThisIteration = framesInClientFormatCap;
11864 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration);
11865 if (result != MA_SUCCESS) {
11869 if (clientFramesProcessedThisIteration > 0) {
11870 ma_device__on_data(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */
11873 pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
11874 totalDeviceFramesProcessed += deviceFramesProcessedThisIteration;
11875 totalClientFramesProcessed += clientFramesProcessedThisIteration;
11877 if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) {
11878 break; /* We're done. */
11884 static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB)
11887 ma_uint32 totalDeviceFramesProcessed = 0;
11888 const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;
11890 MA_ASSERT(pDevice != NULL);
11891 MA_ASSERT(frameCountInDeviceFormat > 0);
11892 MA_ASSERT(pFramesInDeviceFormat != NULL);
11893 MA_ASSERT(pRB != NULL);
11895 /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */
11897 ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
11898 ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
11899 ma_uint64 framesProcessedInDeviceFormat;
11900 ma_uint64 framesProcessedInClientFormat;
11901 void* pFramesInClientFormat;
11903 result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat);
11904 if (result != MA_SUCCESS) {
11905 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer.", result);
11909 if (framesToProcessInClientFormat == 0) {
11910 if (ma_pcm_rb_pointer_distance(pRB) == (ma_int32)ma_pcm_rb_get_subbuffer_size(pRB)) {
11911 break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */
11916 framesProcessedInDeviceFormat = framesToProcessInDeviceFormat;
11917 framesProcessedInClientFormat = framesToProcessInClientFormat;
11918 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat);
11919 if (result != MA_SUCCESS) {
11923 result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat, pFramesInClientFormat); /* Safe cast. */
11924 if (result != MA_SUCCESS) {
11925 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer.", result);
11929 pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
11930 totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */
11932 /* We're done when we're unable to process any client nor device frames. */
11933 if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) {
11941 static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB)
11944 ma_uint8 playbackFramesInExternalFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
11945 ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
11946 ma_uint32 totalFramesToReadFromClient;
11947 ma_uint32 totalFramesReadFromClient;
11948 ma_uint32 totalFramesReadOut = 0;
11950 MA_ASSERT(pDevice != NULL);
11951 MA_ASSERT(frameCount > 0);
11952 MA_ASSERT(pFramesInInternalFormat != NULL);
11953 MA_ASSERT(pRB != NULL);
11956 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
11957 the whole frameCount frames we just use silence instead for the input data.
11959 MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames));
11961 /* We need to calculate how many output frames are required to be read from the client to completely fill frameCount internal frames. */
11962 totalFramesToReadFromClient = (ma_uint32)ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, frameCount);
11963 totalFramesReadFromClient = 0;
11964 while (totalFramesReadFromClient < totalFramesToReadFromClient && ma_device_is_started(pDevice)) {
11965 ma_uint32 framesRemainingFromClient;
11966 ma_uint32 framesToProcessFromClient;
11967 ma_uint32 inputFrameCount;
11968 void* pInputFrames;
11970 framesRemainingFromClient = (totalFramesToReadFromClient - totalFramesReadFromClient);
11971 framesToProcessFromClient = sizeof(playbackFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
11972 if (framesToProcessFromClient > framesRemainingFromClient) {
11973 framesToProcessFromClient = framesRemainingFromClient;
11976 /* We need to grab captured samples before firing the callback. If there's not enough input samples we just pass silence. */
11977 inputFrameCount = framesToProcessFromClient;
11978 result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames);
11979 if (result == MA_SUCCESS) {
11980 if (inputFrameCount > 0) {
11981 /* Use actual input frames. */
11982 ma_device__on_data(pDevice, playbackFramesInExternalFormat, pInputFrames, inputFrameCount);
11984 if (ma_pcm_rb_pointer_distance(pRB) == 0) {
11985 break; /* Underrun. */
11989 /* We're done with the captured samples. */
11990 result = ma_pcm_rb_commit_read(pRB, inputFrameCount, pInputFrames);
11991 if (result != MA_SUCCESS) {
11992 break; /* Don't know what to do here... Just abandon ship. */
11995 /* Use silent input frames. */
11996 inputFrameCount = ma_min(
11997 sizeof(playbackFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels),
11998 sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)
12001 ma_device__on_data(pDevice, playbackFramesInExternalFormat, silentInputFrames, inputFrameCount);
12004 /* We have samples in external format so now we need to convert to internal format and output to the device. */
12006 ma_uint64 framesConvertedIn = inputFrameCount;
12007 ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut);
12008 ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackFramesInExternalFormat, &framesConvertedIn, pFramesInInternalFormat, &framesConvertedOut);
12010 totalFramesReadFromClient += (ma_uint32)framesConvertedIn; /* Safe cast. */
12011 totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */
12012 pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
12019 /* A helper for changing the state of the device. */
12020 static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_uint32 newState)
12022 c89atomic_exchange_32(&pDevice->state, newState);
12027 GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
12028 GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
12029 /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
12030 /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
12035 MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */
12038 for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) {
12039 if (g_maFormatPriorities[i] == format) {
12044 /* Getting here means the format could not be found or is equal to ma_format_unknown. */
12045 return (ma_uint32)-1;
12048 static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType);
12051 static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor)
12053 if (pDeviceDescriptor == NULL) {
12057 if (pDeviceDescriptor->format == ma_format_unknown) {
12061 if (pDeviceDescriptor->channels < MA_MIN_CHANNELS || pDeviceDescriptor->channels > MA_MAX_CHANNELS) {
12065 if (pDeviceDescriptor->sampleRate == 0) {
12073 static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice)
12075 ma_result result = MA_SUCCESS;
12076 ma_bool32 exitLoop = MA_FALSE;
12077 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
12078 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
12079 ma_uint32 capturedDeviceDataCapInFrames = 0;
12080 ma_uint32 playbackDeviceDataCapInFrames = 0;
12082 MA_ASSERT(pDevice != NULL);
12084 /* Just some quick validation on the device type and the available callbacks. */
12085 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
12086 if (pDevice->pContext->callbacks.onDeviceRead == NULL) {
12087 return MA_NOT_IMPLEMENTED;
12090 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
12093 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
12094 if (pDevice->pContext->callbacks.onDeviceWrite == NULL) {
12095 return MA_NOT_IMPLEMENTED;
12098 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
12101 /* NOTE: The device was started outside of this function, in the worker thread. */
12103 while (ma_device_get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
12104 switch (pDevice->type) {
12105 case ma_device_type_duplex:
12107 /* The process is: onDeviceRead() -> convert -> callback -> convert -> onDeviceWrite() */
12108 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
12109 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
12111 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
12112 ma_uint32 capturedDeviceFramesRemaining;
12113 ma_uint32 capturedDeviceFramesProcessed;
12114 ma_uint32 capturedDeviceFramesToProcess;
12115 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
12116 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
12117 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
12120 result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
12121 if (result != MA_SUCCESS) {
12122 exitLoop = MA_TRUE;
12126 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
12127 capturedDeviceFramesProcessed = 0;
12129 /* At this point we have our captured data in device format and we now need to convert it to client format. */
12131 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
12132 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
12133 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
12134 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
12135 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
12136 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
12137 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
12139 /* Convert capture data from device format to client format. */
12140 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
12141 if (result != MA_SUCCESS) {
12146 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
12147 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
12149 if (capturedClientFramesToProcessThisIteration == 0) {
12153 ma_device__on_data(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
12155 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
12156 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
12158 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
12160 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
12161 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
12162 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
12163 if (result != MA_SUCCESS) {
12167 result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
12168 if (result != MA_SUCCESS) {
12169 exitLoop = MA_TRUE;
12173 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
12174 if (capturedClientFramesToProcessThisIteration == 0) {
12179 /* In case an error happened from ma_device_write__null()... */
12180 if (result != MA_SUCCESS) {
12181 exitLoop = MA_TRUE;
12186 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
12190 case ma_device_type_capture:
12191 case ma_device_type_loopback:
12193 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
12194 ma_uint32 framesReadThisPeriod = 0;
12195 while (framesReadThisPeriod < periodSizeInFrames) {
12196 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
12197 ma_uint32 framesProcessed;
12198 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
12199 if (framesToReadThisIteration > capturedDeviceDataCapInFrames) {
12200 framesToReadThisIteration = capturedDeviceDataCapInFrames;
12203 result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed);
12204 if (result != MA_SUCCESS) {
12205 exitLoop = MA_TRUE;
12209 ma_device__send_frames_to_client(pDevice, framesProcessed, capturedDeviceData);
12211 framesReadThisPeriod += framesProcessed;
12215 case ma_device_type_playback:
12217 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
12218 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
12219 ma_uint32 framesWrittenThisPeriod = 0;
12220 while (framesWrittenThisPeriod < periodSizeInFrames) {
12221 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
12222 ma_uint32 framesProcessed;
12223 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
12224 if (framesToWriteThisIteration > playbackDeviceDataCapInFrames) {
12225 framesToWriteThisIteration = playbackDeviceDataCapInFrames;
12228 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData);
12230 result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed);
12231 if (result != MA_SUCCESS) {
12232 exitLoop = MA_TRUE;
12236 framesWrittenThisPeriod += framesProcessed;
12240 /* Should never get here. */
12250 /*******************************************************************************
12254 *******************************************************************************/
12257 #define MA_DEVICE_OP_NONE__NULL 0
12258 #define MA_DEVICE_OP_START__NULL 1
12259 #define MA_DEVICE_OP_SUSPEND__NULL 2
12260 #define MA_DEVICE_OP_KILL__NULL 3
12262 static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData)
12264 ma_device* pDevice = (ma_device*)pData;
12265 MA_ASSERT(pDevice != NULL);
12267 for (;;) { /* Keep the thread alive until the device is uninitialized. */
12268 ma_uint32 operation;
12270 /* Wait for an operation to be requested. */
12271 ma_event_wait(&pDevice->null_device.operationEvent);
12273 /* At this point an event should have been triggered. */
12274 operation = pDevice->null_device.operation;
12276 /* Starting the device needs to put the thread into a loop. */
12277 if (operation == MA_DEVICE_OP_START__NULL) {
12278 /* Reset the timer just in case. */
12279 ma_timer_init(&pDevice->null_device.timer);
12281 /* Getting here means a suspend or kill operation has been requested. */
12282 pDevice->null_device.operationResult = MA_SUCCESS;
12283 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
12284 ma_semaphore_release(&pDevice->null_device.operationSemaphore);
12288 /* Suspending the device means we need to stop the timer and just continue the loop. */
12289 if (operation == MA_DEVICE_OP_SUSPEND__NULL) {
12290 /* We need to add the current run time to the prior run time, then reset the timer. */
12291 pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer);
12292 ma_timer_init(&pDevice->null_device.timer);
12295 pDevice->null_device.operationResult = MA_SUCCESS;
12296 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
12297 ma_semaphore_release(&pDevice->null_device.operationSemaphore);
12301 /* Killing the device means we need to get out of this loop so that this thread can terminate. */
12302 if (operation == MA_DEVICE_OP_KILL__NULL) {
12303 pDevice->null_device.operationResult = MA_SUCCESS;
12304 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
12305 ma_semaphore_release(&pDevice->null_device.operationSemaphore);
12309 /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */
12310 if (operation == MA_DEVICE_OP_NONE__NULL) {
12311 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). */
12312 pDevice->null_device.operationResult = MA_INVALID_OPERATION;
12313 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
12314 ma_semaphore_release(&pDevice->null_device.operationSemaphore);
12315 continue; /* Continue the loop. Don't terminate. */
12319 return (ma_thread_result)0;
12322 static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation)
12327 TODO: Need to review this and consider just using mutual exclusion. I think the original motivation
12328 for this was to just post the event to a queue and return immediately, but that has since changed
12329 and now this function is synchronous. I think this can be simplified to just use a mutex.
12333 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
12334 to support queing of operations.
12336 result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore);
12337 if (result != MA_SUCCESS) {
12338 return result; /* Failed to wait for the event. */
12342 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
12343 signal an event to the worker thread to let it know that it can start work.
12345 pDevice->null_device.operation = operation;
12347 /* Once the operation code has been set, the worker thread can start work. */
12348 if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) {
12352 /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */
12353 if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) {
12357 return pDevice->null_device.operationResult;
12360 static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice)
12362 ma_uint32 internalSampleRate;
12363 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
12364 internalSampleRate = pDevice->capture.internalSampleRate;
12366 internalSampleRate = pDevice->playback.internalSampleRate;
12369 return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate);
12372 static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
12374 ma_bool32 cbResult = MA_TRUE;
12376 MA_ASSERT(pContext != NULL);
12377 MA_ASSERT(callback != NULL);
12381 ma_device_info deviceInfo;
12382 MA_ZERO_OBJECT(&deviceInfo);
12383 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1);
12384 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
12389 ma_device_info deviceInfo;
12390 MA_ZERO_OBJECT(&deviceInfo);
12391 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1);
12392 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
12395 (void)cbResult; /* Silence a static analysis warning. */
12400 static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
12402 MA_ASSERT(pContext != NULL);
12404 if (pDeviceID != NULL && pDeviceID->nullbackend != 0) {
12405 return MA_NO_DEVICE; /* Don't know the device. */
12408 /* Name / Description */
12409 if (deviceType == ma_device_type_playback) {
12410 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1);
12412 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1);
12415 /* Support everything on the null backend. */
12416 pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown;
12417 pDeviceInfo->nativeDataFormats[0].channels = 0;
12418 pDeviceInfo->nativeDataFormats[0].sampleRate = 0;
12419 pDeviceInfo->nativeDataFormats[0].flags = 0;
12420 pDeviceInfo->nativeDataFormatCount = 1;
12427 static ma_result ma_device_uninit__null(ma_device* pDevice)
12429 MA_ASSERT(pDevice != NULL);
12431 /* Keep it clean and wait for the device thread to finish before returning. */
12432 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL);
12434 /* Wait for the thread to finish before continuing. */
12435 ma_thread_wait(&pDevice->null_device.deviceThread);
12437 /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */
12438 ma_semaphore_uninit(&pDevice->null_device.operationSemaphore);
12439 ma_event_uninit(&pDevice->null_device.operationCompletionEvent);
12440 ma_event_uninit(&pDevice->null_device.operationEvent);
12445 static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
12449 MA_ASSERT(pDevice != NULL);
12451 MA_ZERO_OBJECT(&pDevice->null_device);
12453 if (pConfig->deviceType == ma_device_type_loopback) {
12454 return MA_DEVICE_TYPE_NOT_SUPPORTED;
12457 /* The null backend supports everything exactly as we specify it. */
12458 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
12459 pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT;
12460 pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS;
12461 pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE;
12463 if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) {
12464 ma_get_standard_channel_map(ma_standard_channel_map_default, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
12467 pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
12470 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
12471 pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT;
12472 pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS;
12473 pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE;
12475 if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) {
12476 ma_get_standard_channel_map(ma_standard_channel_map_default, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
12479 pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
12483 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
12484 first period is "written" to it, and then stopped in ma_device_stop__null().
12486 result = ma_event_init(&pDevice->null_device.operationEvent);
12487 if (result != MA_SUCCESS) {
12491 result = ma_event_init(&pDevice->null_device.operationCompletionEvent);
12492 if (result != MA_SUCCESS) {
12496 result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore); /* <-- It's important that the initial value is set to 1. */
12497 if (result != MA_SUCCESS) {
12501 result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice);
12502 if (result != MA_SUCCESS) {
12509 static ma_result ma_device_start__null(ma_device* pDevice)
12511 MA_ASSERT(pDevice != NULL);
12513 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL);
12515 c89atomic_exchange_32(&pDevice->null_device.isStarted, MA_TRUE);
12519 static ma_result ma_device_stop__null(ma_device* pDevice)
12521 MA_ASSERT(pDevice != NULL);
12523 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL);
12525 c89atomic_exchange_32(&pDevice->null_device.isStarted, MA_FALSE);
12529 static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
12531 ma_result result = MA_SUCCESS;
12532 ma_uint32 totalPCMFramesProcessed;
12533 ma_bool32 wasStartedOnEntry;
12535 if (pFramesWritten != NULL) {
12536 *pFramesWritten = 0;
12539 wasStartedOnEntry = c89atomic_load_32(&pDevice->null_device.isStarted);
12541 /* Keep going until everything has been read. */
12542 totalPCMFramesProcessed = 0;
12543 while (totalPCMFramesProcessed < frameCount) {
12544 ma_uint64 targetFrame;
12546 /* If there are any frames remaining in the current period, consume those first. */
12547 if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) {
12548 ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
12549 ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback;
12550 if (framesToProcess > framesRemaining) {
12551 framesToProcess = framesRemaining;
12554 /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */
12557 pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess;
12558 totalPCMFramesProcessed += framesToProcess;
12561 /* 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. */
12562 if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) {
12563 pDevice->null_device.currentPeriodFramesRemainingPlayback = 0;
12565 if (!c89atomic_load_32(&pDevice->null_device.isStarted) && !wasStartedOnEntry) {
12566 result = ma_device_start__null(pDevice);
12567 if (result != MA_SUCCESS) {
12573 /* If we've consumed the whole buffer we can return now. */
12574 MA_ASSERT(totalPCMFramesProcessed <= frameCount);
12575 if (totalPCMFramesProcessed == frameCount) {
12579 /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
12580 targetFrame = pDevice->null_device.lastProcessedFramePlayback;
12582 ma_uint64 currentFrame;
12584 /* Stop waiting if the device has been stopped. */
12585 if (!c89atomic_load_32(&pDevice->null_device.isStarted)) {
12589 currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
12590 if (currentFrame >= targetFrame) {
12594 /* Getting here means we haven't yet reached the target sample, so continue waiting. */
12598 pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalPeriodSizeInFrames;
12599 pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalPeriodSizeInFrames;
12602 if (pFramesWritten != NULL) {
12603 *pFramesWritten = totalPCMFramesProcessed;
12609 static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
12611 ma_result result = MA_SUCCESS;
12612 ma_uint32 totalPCMFramesProcessed;
12614 if (pFramesRead != NULL) {
12618 /* Keep going until everything has been read. */
12619 totalPCMFramesProcessed = 0;
12620 while (totalPCMFramesProcessed < frameCount) {
12621 ma_uint64 targetFrame;
12623 /* If there are any frames remaining in the current period, consume those first. */
12624 if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) {
12625 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
12626 ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
12627 ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture;
12628 if (framesToProcess > framesRemaining) {
12629 framesToProcess = framesRemaining;
12632 /* We need to ensure the output buffer is zeroed. */
12633 MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf);
12635 pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess;
12636 totalPCMFramesProcessed += framesToProcess;
12639 /* 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. */
12640 if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) {
12641 pDevice->null_device.currentPeriodFramesRemainingCapture = 0;
12644 /* If we've consumed the whole buffer we can return now. */
12645 MA_ASSERT(totalPCMFramesProcessed <= frameCount);
12646 if (totalPCMFramesProcessed == frameCount) {
12650 /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
12651 targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames;
12653 ma_uint64 currentFrame;
12655 /* Stop waiting if the device has been stopped. */
12656 if (!c89atomic_load_32(&pDevice->null_device.isStarted)) {
12660 currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
12661 if (currentFrame >= targetFrame) {
12665 /* Getting here means we haven't yet reached the target sample, so continue waiting. */
12669 pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalPeriodSizeInFrames;
12670 pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames;
12673 if (pFramesRead != NULL) {
12674 *pFramesRead = totalPCMFramesProcessed;
12680 static ma_result ma_context_uninit__null(ma_context* pContext)
12682 MA_ASSERT(pContext != NULL);
12683 MA_ASSERT(pContext->backend == ma_backend_null);
12689 static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
12691 MA_ASSERT(pContext != NULL);
12696 pCallbacks->onContextInit = ma_context_init__null;
12697 pCallbacks->onContextUninit = ma_context_uninit__null;
12698 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null;
12699 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null;
12700 pCallbacks->onDeviceInit = ma_device_init__null;
12701 pCallbacks->onDeviceUninit = ma_device_uninit__null;
12702 pCallbacks->onDeviceStart = ma_device_start__null;
12703 pCallbacks->onDeviceStop = ma_device_stop__null;
12704 pCallbacks->onDeviceRead = ma_device_read__null;
12705 pCallbacks->onDeviceWrite = ma_device_write__null;
12706 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. */
12708 /* The null backend always works. */
12715 /*******************************************************************************
12719 *******************************************************************************/
12720 #if defined(MA_WIN32)
12721 #if defined(MA_WIN32_DESKTOP)
12722 #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit)
12723 #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)()
12724 #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv)
12725 #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv)
12726 #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar)
12728 #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit)
12729 #define ma_CoUninitialize(pContext) CoUninitialize()
12730 #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv)
12731 #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv)
12732 #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar)
12735 #if !defined(MAXULONG_PTR) && !defined(__WATCOMC__)
12736 typedef size_t DWORD_PTR;
12739 #if !defined(WAVE_FORMAT_44M08)
12740 #define WAVE_FORMAT_44M08 0x00000100
12741 #define WAVE_FORMAT_44S08 0x00000200
12742 #define WAVE_FORMAT_44M16 0x00000400
12743 #define WAVE_FORMAT_44S16 0x00000800
12744 #define WAVE_FORMAT_48M08 0x00001000
12745 #define WAVE_FORMAT_48S08 0x00002000
12746 #define WAVE_FORMAT_48M16 0x00004000
12747 #define WAVE_FORMAT_48S16 0x00008000
12748 #define WAVE_FORMAT_96M08 0x00010000
12749 #define WAVE_FORMAT_96S08 0x00020000
12750 #define WAVE_FORMAT_96M16 0x00040000
12751 #define WAVE_FORMAT_96S16 0x00080000
12754 #ifndef SPEAKER_FRONT_LEFT
12755 #define SPEAKER_FRONT_LEFT 0x1
12756 #define SPEAKER_FRONT_RIGHT 0x2
12757 #define SPEAKER_FRONT_CENTER 0x4
12758 #define SPEAKER_LOW_FREQUENCY 0x8
12759 #define SPEAKER_BACK_LEFT 0x10
12760 #define SPEAKER_BACK_RIGHT 0x20
12761 #define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
12762 #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
12763 #define SPEAKER_BACK_CENTER 0x100
12764 #define SPEAKER_SIDE_LEFT 0x200
12765 #define SPEAKER_SIDE_RIGHT 0x400
12766 #define SPEAKER_TOP_CENTER 0x800
12767 #define SPEAKER_TOP_FRONT_LEFT 0x1000
12768 #define SPEAKER_TOP_FRONT_CENTER 0x2000
12769 #define SPEAKER_TOP_FRONT_RIGHT 0x4000
12770 #define SPEAKER_TOP_BACK_LEFT 0x8000
12771 #define SPEAKER_TOP_BACK_CENTER 0x10000
12772 #define SPEAKER_TOP_BACK_RIGHT 0x20000
12776 The SDK that comes with old versions of MSVC (VC6, for example) does not appear to define WAVEFORMATEXTENSIBLE. We
12777 define our own implementation in this case.
12779 #if (defined(_MSC_VER) && !defined(_WAVEFORMATEXTENSIBLE_)) || defined(__DMC__)
12782 WAVEFORMATEX Format;
12785 WORD wValidBitsPerSample;
12786 WORD wSamplesPerBlock;
12789 DWORD dwChannelMask;
12791 } WAVEFORMATEXTENSIBLE;
12794 #ifndef WAVE_FORMAT_EXTENSIBLE
12795 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE
12798 #ifndef WAVE_FORMAT_IEEE_FLOAT
12799 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
12802 /* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
12803 static ma_uint8 ma_channel_id_to_ma__win32(DWORD id)
12807 case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
12808 case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
12809 case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
12810 case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
12811 case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
12812 case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
12813 case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
12814 case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
12815 case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
12816 case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
12817 case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
12818 case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
12819 case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
12820 case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
12821 case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
12822 case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
12823 case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
12824 case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
12829 /* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */
12830 static DWORD ma_channel_id_to_win32(DWORD id)
12834 case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER;
12835 case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT;
12836 case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT;
12837 case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER;
12838 case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY;
12839 case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT;
12840 case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT;
12841 case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER;
12842 case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER;
12843 case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER;
12844 case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT;
12845 case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT;
12846 case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER;
12847 case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT;
12848 case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER;
12849 case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT;
12850 case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT;
12851 case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER;
12852 case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT;
12857 /* Converts a channel mapping to a Win32-style channel mask. */
12858 static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap, ma_uint32 channels)
12860 DWORD dwChannelMask = 0;
12861 ma_uint32 iChannel;
12863 for (iChannel = 0; iChannel < channels; ++iChannel) {
12864 dwChannelMask |= ma_channel_id_to_win32(pChannelMap[iChannel]);
12867 return dwChannelMask;
12870 /* Converts a Win32-style channel mask to a miniaudio channel map. */
12871 static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap)
12873 if (channels == 1 && dwChannelMask == 0) {
12874 pChannelMap[0] = MA_CHANNEL_MONO;
12875 } else if (channels == 2 && dwChannelMask == 0) {
12876 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
12877 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
12879 if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) {
12880 pChannelMap[0] = MA_CHANNEL_MONO;
12882 /* Just iterate over each bit. */
12883 ma_uint32 iChannel = 0;
12886 for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) {
12887 DWORD bitValue = (dwChannelMask & (1UL << iBit));
12888 if (bitValue != 0) {
12889 /* The bit is set. */
12890 pChannelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue);
12899 static ma_bool32 ma_is_guid_equal(const void* a, const void* b)
12901 return IsEqualGUID(*(const GUID*)a, *(const GUID*)b);
12904 #define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b)
12907 static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid)
12909 static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
12910 return ma_is_guid_equal(guid, &nullguid);
12913 static ma_format ma_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF)
12915 MA_ASSERT(pWF != NULL);
12917 if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
12918 const WAVEFORMATEXTENSIBLE* pWFEX = (const WAVEFORMATEXTENSIBLE*)pWF;
12919 if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) {
12920 if (pWFEX->Samples.wValidBitsPerSample == 32) {
12921 return ma_format_s32;
12923 if (pWFEX->Samples.wValidBitsPerSample == 24) {
12924 if (pWFEX->Format.wBitsPerSample == 32) {
12925 /*return ma_format_s24_32;*/
12927 if (pWFEX->Format.wBitsPerSample == 24) {
12928 return ma_format_s24;
12931 if (pWFEX->Samples.wValidBitsPerSample == 16) {
12932 return ma_format_s16;
12934 if (pWFEX->Samples.wValidBitsPerSample == 8) {
12935 return ma_format_u8;
12938 if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
12939 if (pWFEX->Samples.wValidBitsPerSample == 32) {
12940 return ma_format_f32;
12943 if (pWFEX->Samples.wValidBitsPerSample == 64) {
12944 return ma_format_f64;
12949 if (pWF->wFormatTag == WAVE_FORMAT_PCM) {
12950 if (pWF->wBitsPerSample == 32) {
12951 return ma_format_s32;
12953 if (pWF->wBitsPerSample == 24) {
12954 return ma_format_s24;
12956 if (pWF->wBitsPerSample == 16) {
12957 return ma_format_s16;
12959 if (pWF->wBitsPerSample == 8) {
12960 return ma_format_u8;
12963 if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
12964 if (pWF->wBitsPerSample == 32) {
12965 return ma_format_f32;
12967 if (pWF->wBitsPerSample == 64) {
12968 /*return ma_format_f64;*/
12973 return ma_format_unknown;
12978 /*******************************************************************************
12982 *******************************************************************************/
12983 #ifdef MA_HAS_WASAPI
12985 #if defined(_MSC_VER)
12986 #pragma warning(push)
12987 #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */
12989 #include <audioclient.h>
12990 #include <mmdeviceapi.h>
12991 #if defined(_MSC_VER)
12992 #pragma warning(pop)
12996 static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType);
12998 /* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */
12999 #define MA_WIN32_WINNT_VISTA 0x0600
13000 #define MA_VER_MINORVERSION 0x01
13001 #define MA_VER_MAJORVERSION 0x02
13002 #define MA_VER_SERVICEPACKMAJOR 0x20
13003 #define MA_VER_GREATER_EQUAL 0x03
13006 DWORD dwOSVersionInfoSize;
13007 DWORD dwMajorVersion;
13008 DWORD dwMinorVersion;
13009 DWORD dwBuildNumber;
13010 DWORD dwPlatformId;
13011 WCHAR szCSDVersion[128];
13012 WORD wServicePackMajor;
13013 WORD wServicePackMinor;
13017 } ma_OSVERSIONINFOEXW;
13019 typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask);
13020 typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask);
13023 #ifndef PROPERTYKEY_DEFINED
13024 #define PROPERTYKEY_DEFINED
13025 #ifndef __WATCOMC__
13034 /* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */
13035 static MA_INLINE void ma_PropVariantInit(PROPVARIANT* pProp)
13037 MA_ZERO_OBJECT(pProp);
13041 static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14};
13042 static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0};
13044 static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */
13045 #ifndef MA_WIN32_DESKTOP
13046 static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */
13049 static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */
13050 static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */
13051 static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */
13052 static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */
13053 static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */
13054 static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */
13055 #ifndef MA_WIN32_DESKTOP
13056 static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */
13057 static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */
13058 static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */
13061 static const IID MA_CLSID_MMDeviceEnumerator_Instance = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */
13062 static const IID MA_IID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */
13064 #define MA_CLSID_MMDeviceEnumerator MA_CLSID_MMDeviceEnumerator_Instance
13065 #define MA_IID_IMMDeviceEnumerator MA_IID_IMMDeviceEnumerator_Instance
13067 #define MA_CLSID_MMDeviceEnumerator &MA_CLSID_MMDeviceEnumerator_Instance
13068 #define MA_IID_IMMDeviceEnumerator &MA_IID_IMMDeviceEnumerator_Instance
13071 typedef struct ma_IUnknown ma_IUnknown;
13072 #ifdef MA_WIN32_DESKTOP
13073 #define MA_MM_DEVICE_STATE_ACTIVE 1
13074 #define MA_MM_DEVICE_STATE_DISABLED 2
13075 #define MA_MM_DEVICE_STATE_NOTPRESENT 4
13076 #define MA_MM_DEVICE_STATE_UNPLUGGED 8
13078 typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator;
13079 typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection;
13080 typedef struct ma_IMMDevice ma_IMMDevice;
13082 typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler;
13083 typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation;
13085 typedef struct ma_IPropertyStore ma_IPropertyStore;
13086 typedef struct ma_IAudioClient ma_IAudioClient;
13087 typedef struct ma_IAudioClient2 ma_IAudioClient2;
13088 typedef struct ma_IAudioClient3 ma_IAudioClient3;
13089 typedef struct ma_IAudioRenderClient ma_IAudioRenderClient;
13090 typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient;
13092 typedef ma_int64 MA_REFERENCE_TIME;
13094 #define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000
13095 #define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000
13096 #define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000
13097 #define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000
13098 #define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
13099 #define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
13100 #define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
13101 #define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000
13102 #define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000
13103 #define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000
13105 /* Buffer flags. */
13106 #define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1
13107 #define MA_AUDCLNT_BUFFERFLAGS_SILENT 2
13108 #define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4
13120 ma_eMultimedia = 1,
13121 ma_eCommunications = 2
13126 MA_AUDCLNT_SHAREMODE_SHARED,
13127 MA_AUDCLNT_SHAREMODE_EXCLUSIVE
13128 } MA_AUDCLNT_SHAREMODE;
13132 MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */
13133 } MA_AUDIO_STREAM_CATEGORY;
13139 MA_AUDIO_STREAM_CATEGORY eCategory;
13140 } ma_AudioClientProperties;
13146 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject);
13147 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis);
13148 ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis);
13152 ma_IUnknownVtbl* lpVtbl;
13154 static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13155 static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13156 static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); }
13158 #ifdef MA_WIN32_DESKTOP
13159 /* IMMNotificationClient */
13163 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject);
13164 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis);
13165 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis);
13167 /* IMMNotificationClient */
13168 HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState);
13169 HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID);
13170 HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID);
13171 HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID);
13172 HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key);
13173 } ma_IMMNotificationClientVtbl;
13175 /* IMMDeviceEnumerator */
13179 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject);
13180 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis);
13181 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis);
13183 /* IMMDeviceEnumerator */
13184 HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices);
13185 HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint);
13186 HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice);
13187 HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
13188 HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
13189 } ma_IMMDeviceEnumeratorVtbl;
13190 struct ma_IMMDeviceEnumerator
13192 ma_IMMDeviceEnumeratorVtbl* lpVtbl;
13194 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13195 static MA_INLINE ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13196 static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); }
13197 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); }
13198 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); }
13199 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); }
13200 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); }
13201 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); }
13204 /* IMMDeviceCollection */
13208 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject);
13209 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis);
13210 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis);
13212 /* IMMDeviceCollection */
13213 HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices);
13214 HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice);
13215 } ma_IMMDeviceCollectionVtbl;
13216 struct ma_IMMDeviceCollection
13218 ma_IMMDeviceCollectionVtbl* lpVtbl;
13220 static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13221 static MA_INLINE ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13222 static MA_INLINE ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); }
13223 static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); }
13224 static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); }
13231 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject);
13232 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis);
13233 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis);
13236 HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface);
13237 HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties);
13238 HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, LPWSTR *pID);
13239 HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState);
13240 } ma_IMMDeviceVtbl;
13241 struct ma_IMMDevice
13243 ma_IMMDeviceVtbl* lpVtbl;
13245 static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13246 static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13247 static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); }
13248 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); }
13249 static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); }
13250 static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, LPWSTR *pID) { return pThis->lpVtbl->GetId(pThis, pID); }
13251 static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); }
13253 /* IActivateAudioInterfaceAsyncOperation */
13257 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject);
13258 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
13259 ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
13261 /* IActivateAudioInterfaceAsyncOperation */
13262 HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface);
13263 } ma_IActivateAudioInterfaceAsyncOperationVtbl;
13264 struct ma_IActivateAudioInterfaceAsyncOperation
13266 ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl;
13268 static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13269 static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13270 static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); }
13271 static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); }
13274 /* IPropertyStore */
13278 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject);
13279 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis);
13280 ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis);
13282 /* IPropertyStore */
13283 HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount);
13284 HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey);
13285 HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar);
13286 HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar);
13287 HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis);
13288 } ma_IPropertyStoreVtbl;
13289 struct ma_IPropertyStore
13291 ma_IPropertyStoreVtbl* lpVtbl;
13293 static MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13294 static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13295 static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); }
13296 static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); }
13297 static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); }
13298 static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); }
13299 static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); }
13300 static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); }
13307 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject);
13308 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis);
13309 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis);
13312 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);
13313 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames);
13314 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency);
13315 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames);
13316 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
13317 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat);
13318 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
13319 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis);
13320 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis);
13321 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis);
13322 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle);
13323 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp);
13324 } ma_IAudioClientVtbl;
13325 struct ma_IAudioClient
13327 ma_IAudioClientVtbl* lpVtbl;
13329 static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13330 static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13331 static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); }
13332 static MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }
13333 static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
13334 static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
13335 static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
13336 static 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); }
13337 static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
13338 static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
13339 static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); }
13340 static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); }
13341 static MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); }
13342 static MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
13343 static MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
13345 /* IAudioClient2 */
13349 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject);
13350 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis);
13351 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis);
13354 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);
13355 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames);
13356 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency);
13357 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames);
13358 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
13359 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat);
13360 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
13361 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis);
13362 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis);
13363 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis);
13364 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle);
13365 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp);
13367 /* IAudioClient2 */
13368 HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
13369 HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties);
13370 HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
13371 } ma_IAudioClient2Vtbl;
13372 struct ma_IAudioClient2
13374 ma_IAudioClient2Vtbl* lpVtbl;
13376 static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13377 static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13378 static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); }
13379 static MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }
13380 static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
13381 static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
13382 static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
13383 static 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); }
13384 static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
13385 static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
13386 static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); }
13387 static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); }
13388 static MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); }
13389 static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
13390 static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
13391 static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
13392 static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
13393 static 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); }
13396 /* IAudioClient3 */
13400 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject);
13401 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis);
13402 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis);
13405 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);
13406 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames);
13407 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency);
13408 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames);
13409 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
13410 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat);
13411 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
13412 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis);
13413 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis);
13414 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis);
13415 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle);
13416 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp);
13418 /* IAudioClient2 */
13419 HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
13420 HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties);
13421 HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
13423 /* IAudioClient3 */
13424 HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames);
13425 HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames);
13426 HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
13427 } ma_IAudioClient3Vtbl;
13428 struct ma_IAudioClient3
13430 ma_IAudioClient3Vtbl* lpVtbl;
13432 static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13433 static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13434 static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); }
13435 static MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }
13436 static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
13437 static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
13438 static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
13439 static 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); }
13440 static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
13441 static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
13442 static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); }
13443 static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); }
13444 static MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); }
13445 static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
13446 static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
13447 static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
13448 static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
13449 static 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); }
13450 static 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); }
13451 static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); }
13452 static 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); }
13455 /* IAudioRenderClient */
13459 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject);
13460 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis);
13461 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis);
13463 /* IAudioRenderClient */
13464 HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData);
13465 HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags);
13466 } ma_IAudioRenderClientVtbl;
13467 struct ma_IAudioRenderClient
13469 ma_IAudioRenderClientVtbl* lpVtbl;
13471 static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13472 static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13473 static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); }
13474 static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); }
13475 static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); }
13478 /* IAudioCaptureClient */
13482 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject);
13483 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis);
13484 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis);
13486 /* IAudioRenderClient */
13487 HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition);
13488 HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead);
13489 HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket);
13490 } ma_IAudioCaptureClientVtbl;
13491 struct ma_IAudioCaptureClient
13493 ma_IAudioCaptureClientVtbl* lpVtbl;
13495 static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13496 static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13497 static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); }
13498 static MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, pNumFramesToRead, pFlags, pDevicePosition, pQPCPosition); }
13499 static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); }
13500 static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); }
13502 #ifndef MA_WIN32_DESKTOP
13503 #include <mmdeviceapi.h>
13504 typedef struct ma_completion_handler_uwp ma_completion_handler_uwp;
13509 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject);
13510 ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis);
13511 ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis);
13513 /* IActivateAudioInterfaceCompletionHandler */
13514 HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation);
13515 } ma_completion_handler_uwp_vtbl;
13516 struct ma_completion_handler_uwp
13518 ma_completion_handler_uwp_vtbl* lpVtbl;
13519 MA_ATOMIC ma_uint32 counter;
13523 static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject)
13526 We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To
13527 "implement" this, we just make sure we return pThis when the IAgileObject is requested.
13529 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)) {
13531 return E_NOINTERFACE;
13534 /* Getting here means the IID is IUnknown or IMMNotificationClient. */
13535 *ppObject = (void*)pThis;
13536 ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis);
13540 static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis)
13542 return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1;
13545 static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis)
13547 ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1;
13548 if (newRefCount == 0) {
13549 return 0; /* We don't free anything here because we never allocate the object on the heap. */
13552 return (ULONG)newRefCount;
13555 static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation)
13557 (void)pActivateOperation;
13558 SetEvent(pThis->hEvent);
13563 static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = {
13564 ma_completion_handler_uwp_QueryInterface,
13565 ma_completion_handler_uwp_AddRef,
13566 ma_completion_handler_uwp_Release,
13567 ma_completion_handler_uwp_ActivateCompleted
13570 static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler)
13572 MA_ASSERT(pHandler != NULL);
13573 MA_ZERO_OBJECT(pHandler);
13575 pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance;
13576 pHandler->counter = 1;
13577 pHandler->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
13578 if (pHandler->hEvent == NULL) {
13579 return ma_result_from_GetLastError(GetLastError());
13585 static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler)
13587 if (pHandler->hEvent != NULL) {
13588 CloseHandle(pHandler->hEvent);
13592 static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler)
13594 WaitForSingleObject(pHandler->hEvent, INFINITE);
13596 #endif /* !MA_WIN32_DESKTOP */
13598 /* We need a virtual table for our notification client object that's used for detecting changes to the default device. */
13599 #ifdef MA_WIN32_DESKTOP
13600 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject)
13603 We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else
13604 we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK.
13606 if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) {
13608 return E_NOINTERFACE;
13611 /* Getting here means the IID is IUnknown or IMMNotificationClient. */
13612 *ppObject = (void*)pThis;
13613 ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis);
13617 static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis)
13619 return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1;
13622 static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis)
13624 ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1;
13625 if (newRefCount == 0) {
13626 return 0; /* We don't free anything here because we never allocate the object on the heap. */
13629 return (ULONG)newRefCount;
13632 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState)
13634 ma_bool32 isThisDevice = MA_FALSE;
13635 ma_bool32 isCapture = MA_FALSE;
13636 ma_bool32 isPlayback = MA_FALSE;
13639 #ifdef MA_DEBUG_OUTPUT
13640 printf("IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);
13644 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
13645 that the device is disabled or has been unplugged.
13647 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)) {
13648 isCapture = MA_TRUE;
13649 if (wcscmp(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) {
13650 isThisDevice = MA_TRUE;
13654 if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) {
13655 isPlayback = MA_TRUE;
13656 if (wcscmp(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) {
13657 isThisDevice = MA_TRUE;
13663 If the device ID matches our device we need to mark our device as detached and stop it. When a
13664 device is added in OnDeviceAdded(), we'll restart it. We only mark it as detached if the device
13665 was started at the time of being removed.
13667 if (isThisDevice) {
13668 if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) == 0) {
13670 Unplugged or otherwise unavailable. Mark as detached if we were in a playing state. We'll
13671 use this to determine whether or not we need to automatically start the device when it's
13672 plugged back in again.
13674 if (ma_device_get_state(pThis->pDevice) == MA_STATE_STARTED) {
13676 pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE;
13679 pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE;
13682 ma_device_stop(pThis->pDevice);
13686 if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) {
13687 /* The device was activated. If we were detached, we need to start it again. */
13688 ma_bool8 tryRestartingDevice = MA_FALSE;
13691 if (pThis->pDevice->wasapi.isDetachedPlayback) {
13692 pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;
13693 ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback);
13694 tryRestartingDevice = MA_TRUE;
13699 if (pThis->pDevice->wasapi.isDetachedCapture) {
13700 pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;
13701 ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);
13702 tryRestartingDevice = MA_TRUE;
13706 if (tryRestartingDevice) {
13707 if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) {
13708 ma_device_start(pThis->pDevice);
13717 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
13719 #ifdef MA_DEBUG_OUTPUT
13720 /*printf("IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
13723 /* We don't need to worry about this event for our purposes. */
13729 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
13731 #ifdef MA_DEBUG_OUTPUT
13732 printf("IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");
13735 /* We don't need to worry about this event for our purposes. */
13741 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID)
13743 #ifdef MA_DEBUG_OUTPUT
13744 printf("IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");
13747 /* We only ever use the eConsole role in miniaudio. */
13748 if (role != ma_eConsole) {
13749 #ifdef MA_DEBUG_OUTPUT
13750 printf("[WASAPI] Stream rerouting: role != eConsole\n");
13755 /* We only care about devices with the same data flow and role as the current device. */
13756 if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) ||
13757 (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture)) {
13758 #ifdef MA_DEBUG_OUTPUT
13759 printf("[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n");
13764 /* Don't do automatic stream routing if we're not allowed. */
13765 if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) ||
13766 (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) {
13767 #ifdef MA_DEBUG_OUTPUT
13768 printf("[WASAPI] Stream rerouting abandoned because automatic stream routing has been disabled by the device config.\n");
13774 Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to
13775 AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once
13778 if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) ||
13779 (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) {
13780 #ifdef MA_DEBUG_OUTPUT
13781 printf("[WASAPI] Stream rerouting abandoned because the device shared mode is exclusive.\n");
13790 Second attempt at device rerouting. We're going to retrieve the device's state at the time of
13791 the route change. We're then going to stop the device, reinitialize the device, and then start
13792 it again if the state before stopping was MA_STATE_STARTED.
13795 ma_uint32 previousState = ma_device_get_state(pThis->pDevice);
13796 ma_bool8 restartDevice = MA_FALSE;
13798 if (previousState == MA_STATE_STARTED) {
13799 ma_device_stop(pThis->pDevice);
13800 restartDevice = MA_TRUE;
13803 if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */
13804 if (dataFlow == ma_eRender) {
13805 ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback);
13807 if (pThis->pDevice->wasapi.isDetachedPlayback) {
13808 pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;
13810 if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) {
13811 restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */
13813 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. */
13817 ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);
13819 if (pThis->pDevice->wasapi.isDetachedCapture) {
13820 pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;
13822 if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) {
13823 restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */
13825 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. */
13830 if (restartDevice) {
13831 ma_device_start(pThis->pDevice);
13839 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key)
13841 #ifdef MA_DEBUG_OUTPUT
13842 /*printf("IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
13851 static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = {
13852 ma_IMMNotificationClient_QueryInterface,
13853 ma_IMMNotificationClient_AddRef,
13854 ma_IMMNotificationClient_Release,
13855 ma_IMMNotificationClient_OnDeviceStateChanged,
13856 ma_IMMNotificationClient_OnDeviceAdded,
13857 ma_IMMNotificationClient_OnDeviceRemoved,
13858 ma_IMMNotificationClient_OnDefaultDeviceChanged,
13859 ma_IMMNotificationClient_OnPropertyValueChanged
13861 #endif /* MA_WIN32_DESKTOP */
13863 #ifdef MA_WIN32_DESKTOP
13864 typedef ma_IMMDevice ma_WASAPIDeviceInterface;
13866 typedef ma_IUnknown ma_WASAPIDeviceInterface;
13870 #define MA_CONTEXT_COMMAND_QUIT__WASAPI 1
13871 #define MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI 2
13872 #define MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI 3
13874 static ma_context_command__wasapi ma_context_init_command__wasapi(int code)
13876 ma_context_command__wasapi cmd;
13878 MA_ZERO_OBJECT(&cmd);
13884 static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd)
13886 /* For now we are doing everything synchronously, but I might relax this later if the need arises. */
13888 ma_bool32 isUsingLocalEvent = MA_FALSE;
13889 ma_event localEvent;
13891 MA_ASSERT(pContext != NULL);
13892 MA_ASSERT(pCmd != NULL);
13894 if (pCmd->pEvent == NULL) {
13895 isUsingLocalEvent = MA_TRUE;
13897 result = ma_event_init(&localEvent);
13898 if (result != MA_SUCCESS) {
13899 return result; /* Failed to create the event for this command. */
13903 /* Here is where we add the command to the list. If there's not enough room we'll spin until there is. */
13904 ma_mutex_lock(&pContext->wasapi.commandLock);
13908 /* Spin until we've got some space available. */
13909 while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) {
13913 /* Space is now available. Can safely add to the list. */
13914 index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands);
13915 pContext->wasapi.commands[index] = *pCmd;
13916 pContext->wasapi.commands[index].pEvent = &localEvent;
13917 pContext->wasapi.commandCount += 1;
13919 /* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */
13920 ma_semaphore_release(&pContext->wasapi.commandSem);
13922 ma_mutex_unlock(&pContext->wasapi.commandLock);
13924 if (isUsingLocalEvent) {
13925 ma_event_wait(&localEvent);
13926 ma_event_uninit(&localEvent);
13932 static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd)
13934 ma_result result = MA_SUCCESS;
13936 MA_ASSERT(pContext != NULL);
13937 MA_ASSERT(pCmd != NULL);
13939 result = ma_semaphore_wait(&pContext->wasapi.commandSem);
13940 if (result == MA_SUCCESS) {
13941 ma_mutex_lock(&pContext->wasapi.commandLock);
13943 *pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex];
13944 pContext->wasapi.commandIndex = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands);
13945 pContext->wasapi.commandCount -= 1;
13947 ma_mutex_unlock(&pContext->wasapi.commandLock);
13953 static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData)
13956 ma_context* pContext = (ma_context*)pUserData;
13957 MA_ASSERT(pContext != NULL);
13960 ma_context_command__wasapi cmd;
13961 result = ma_context_next_command__wasapi(pContext, &cmd);
13962 if (result != MA_SUCCESS) {
13968 case MA_CONTEXT_COMMAND_QUIT__WASAPI:
13970 /* Do nothing. Handled after the switch. */
13973 case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI:
13975 if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) {
13976 result = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService));
13978 result = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService));
13982 case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI:
13984 if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) {
13985 if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) {
13986 ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback);
13987 cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL;
13991 if (cmd.data.releaseAudioClient.deviceType == ma_device_type_capture) {
13992 if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) {
13993 ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture);
13994 cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL;
14001 /* Unknown command. Ignore it, but trigger an assert in debug mode so we're aware of it. */
14002 MA_ASSERT(MA_FALSE);
14006 if (cmd.pEvent != NULL) {
14007 ma_event_signal(cmd.pEvent);
14010 if (cmd.code == MA_CONTEXT_COMMAND_QUIT__WASAPI) {
14011 break; /* Received a quit message. Get out of here. */
14015 return (ma_thread_result)0;
14018 static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService)
14021 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI);
14022 cmd.data.createAudioClient.deviceType = deviceType;
14023 cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient;
14024 cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService;
14025 cmd.data.createAudioClient.result = MA_SUCCESS;
14027 result = ma_context_post_command__wasapi(pContext, &cmd); /* This will not return until the command has actually been run. */
14028 if (result != MA_SUCCESS) {
14032 return cmd.data.createAudioClient.result;
14035 #if 0 /* Not used at the moment, but leaving here for future use. */
14036 static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevice, ma_device_type deviceType)
14039 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI);
14040 cmd.data.releaseAudioClient.pDevice = pDevice;
14041 cmd.data.releaseAudioClient.deviceType = deviceType;
14043 result = ma_context_post_command__wasapi(pDevice->pContext, &cmd); /* This will not return until the command has actually been run. */
14044 if (result != MA_SUCCESS) {
14053 static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo)
14055 MA_ASSERT(pWF != NULL);
14056 MA_ASSERT(pInfo != NULL);
14058 if (pInfo->nativeDataFormatCount >= ma_countof(pInfo->nativeDataFormats)) {
14059 return; /* Too many data formats. Need to ignore this one. Don't think this should ever happen with WASAPI. */
14062 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].format = ma_format_from_WAVEFORMATEX(pWF);
14063 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].channels = pWF->nChannels;
14064 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].sampleRate = pWF->nSamplesPerSec;
14065 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].flags = (shareMode == ma_share_mode_exclusive) ? MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE : 0;
14066 pInfo->nativeDataFormatCount += 1;
14069 static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo)
14072 WAVEFORMATEX* pWF = NULL;
14074 MA_ASSERT(pAudioClient != NULL);
14075 MA_ASSERT(pInfo != NULL);
14077 /* Shared Mode. We use GetMixFormat() here. */
14078 hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (WAVEFORMATEX**)&pWF);
14079 if (SUCCEEDED(hr)) {
14080 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo);
14082 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));
14086 Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on
14087 UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on
14088 out, MA_SUCCESS is guaranteed to be returned.
14090 #ifdef MA_WIN32_DESKTOP
14092 ma_IPropertyStore *pProperties;
14095 The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is
14096 correct which will simplify our searching.
14098 hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties);
14099 if (SUCCEEDED(hr)) {
14101 ma_PropVariantInit(&var);
14103 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var);
14104 if (SUCCEEDED(hr)) {
14105 pWF = (WAVEFORMATEX*)var.blob.pBlobData;
14108 In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format
14109 first. If this fails, fall back to a search.
14111 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL);
14112 if (SUCCEEDED(hr)) {
14113 /* The format returned by PKEY_AudioEngine_DeviceFormat is supported. */
14114 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo);
14117 The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel
14118 count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format.
14120 ma_uint32 channels = pInfo->minChannels;
14121 ma_channel defaultChannelMap[MA_MAX_CHANNELS];
14122 WAVEFORMATEXTENSIBLE wf;
14126 /* Make sure we don't overflow the channel map. */
14127 if (channels > MA_MAX_CHANNELS) {
14128 channels = MA_MAX_CHANNELS;
14131 ma_get_standard_channel_map(ma_standard_channel_map_microsoft, channels, defaultChannelMap);
14133 MA_ZERO_OBJECT(&wf);
14134 wf.Format.cbSize = sizeof(wf);
14135 wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
14136 wf.Format.nChannels = (WORD)channels;
14137 wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels);
14140 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) {
14141 ma_format format = g_maFormatPriorities[iFormat];
14142 ma_uint32 iSampleRate;
14144 wf.Format.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8);
14145 wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8);
14146 wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
14147 wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.Format.wBitsPerSample;
14148 if (format == ma_format_f32) {
14149 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
14151 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
14154 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) {
14155 wf.Format.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate];
14157 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*)&wf, NULL);
14158 if (SUCCEEDED(hr)) {
14159 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo);
14170 ma_PropVariantClear(pContext, &var);
14173 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);
14177 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));
14180 ma_IPropertyStore_Release(pProperties);
14182 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));
14190 #ifdef MA_WIN32_DESKTOP
14191 static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType)
14193 if (deviceType == ma_device_type_playback) {
14195 } else if (deviceType == ma_device_type_capture) {
14196 return ma_eCapture;
14198 MA_ASSERT(MA_FALSE);
14199 return ma_eRender; /* Should never hit this. */
14203 static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator)
14206 ma_IMMDeviceEnumerator* pDeviceEnumerator;
14208 MA_ASSERT(pContext != NULL);
14209 MA_ASSERT(ppDeviceEnumerator != NULL);
14211 *ppDeviceEnumerator = NULL; /* Safety. */
14213 hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
14215 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr));
14218 *ppDeviceEnumerator = pDeviceEnumerator;
14223 static LPWSTR ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType)
14226 ma_IMMDevice* pMMDefaultDevice = NULL;
14227 LPWSTR pDefaultDeviceID = NULL;
14228 ma_EDataFlow dataFlow;
14231 MA_ASSERT(pContext != NULL);
14232 MA_ASSERT(pDeviceEnumerator != NULL);
14236 /* Grab the EDataFlow type from the device type. */
14237 dataFlow = ma_device_type_to_EDataFlow(deviceType);
14239 /* The role is always eConsole, but we may make this configurable later. */
14240 role = ma_eConsole;
14242 hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice);
14247 hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID);
14249 ma_IMMDevice_Release(pMMDefaultDevice);
14250 pMMDefaultDevice = NULL;
14256 return pDefaultDeviceID;
14259 static LPWSTR ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */
14262 ma_IMMDeviceEnumerator* pDeviceEnumerator;
14263 LPWSTR pDefaultDeviceID = NULL;
14265 MA_ASSERT(pContext != NULL);
14267 result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator);
14268 if (result != MA_SUCCESS) {
14272 pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
14274 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
14275 return pDefaultDeviceID;
14278 static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice)
14280 ma_IMMDeviceEnumerator* pDeviceEnumerator;
14283 MA_ASSERT(pContext != NULL);
14284 MA_ASSERT(ppMMDevice != NULL);
14286 hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
14288 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.", ma_result_from_HRESULT(hr));
14291 if (pDeviceID == NULL) {
14292 hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice);
14294 hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice);
14297 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
14299 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.", ma_result_from_HRESULT(hr));
14305 static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID)
14307 LPWSTR pDeviceIDString;
14310 MA_ASSERT(pDeviceID != NULL);
14312 hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString);
14313 if (SUCCEEDED(hr)) {
14314 size_t idlen = wcslen(pDeviceIDString);
14315 if (idlen+1 > ma_countof(pDeviceID->wasapi)) {
14316 ma_CoTaskMemFree(pContext, pDeviceIDString);
14317 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. */
14321 MA_COPY_MEMORY(pDeviceID->wasapi, pDeviceIDString, idlen * sizeof(wchar_t));
14322 pDeviceID->wasapi[idlen] = '\0';
14324 ma_CoTaskMemFree(pContext, pDeviceIDString);
14332 static 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)
14337 MA_ASSERT(pContext != NULL);
14338 MA_ASSERT(pMMDevice != NULL);
14339 MA_ASSERT(pInfo != NULL);
14342 result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id);
14343 if (result == MA_SUCCESS) {
14344 if (pDefaultDeviceID != NULL) {
14345 if (wcscmp(pInfo->id.wasapi, pDefaultDeviceID) == 0) {
14346 pInfo->isDefault = MA_TRUE;
14351 /* Description / Friendly Name */
14353 ma_IPropertyStore *pProperties;
14354 hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties);
14355 if (SUCCEEDED(hr)) {
14358 ma_PropVariantInit(&var);
14359 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var);
14360 if (SUCCEEDED(hr)) {
14361 WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE);
14362 ma_PropVariantClear(pContext, &var);
14365 ma_IPropertyStore_Release(pProperties);
14370 if (!onlySimpleInfo) {
14371 ma_IAudioClient* pAudioClient;
14372 hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
14373 if (SUCCEEDED(hr)) {
14374 result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, pInfo);
14376 ma_IAudioClient_Release(pAudioClient);
14379 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));
14386 static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData)
14388 ma_result result = MA_SUCCESS;
14392 LPWSTR pDefaultDeviceID = NULL;
14393 ma_IMMDeviceCollection* pDeviceCollection = NULL;
14395 MA_ASSERT(pContext != NULL);
14396 MA_ASSERT(callback != NULL);
14398 /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */
14399 pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
14401 /* We need to enumerate the devices which returns a device collection. */
14402 hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection);
14403 if (SUCCEEDED(hr)) {
14404 hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount);
14406 result = ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.", ma_result_from_HRESULT(hr));
14410 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
14411 ma_device_info deviceInfo;
14412 ma_IMMDevice* pMMDevice;
14414 MA_ZERO_OBJECT(&deviceInfo);
14416 hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice);
14417 if (SUCCEEDED(hr)) {
14418 result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */
14420 ma_IMMDevice_Release(pMMDevice);
14421 if (result == MA_SUCCESS) {
14422 ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
14423 if (cbResult == MA_FALSE) {
14432 if (pDefaultDeviceID != NULL) {
14433 ma_CoTaskMemFree(pContext, pDefaultDeviceID);
14434 pDefaultDeviceID = NULL;
14437 if (pDeviceCollection != NULL) {
14438 ma_IMMDeviceCollection_Release(pDeviceCollection);
14439 pDeviceCollection = NULL;
14445 static 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)
14450 MA_ASSERT(pContext != NULL);
14451 MA_ASSERT(ppAudioClient != NULL);
14452 MA_ASSERT(ppMMDevice != NULL);
14454 result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice);
14455 if (result != MA_SUCCESS) {
14459 hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)ppAudioClient);
14461 return ma_result_from_HRESULT(hr);
14467 static 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)
14469 ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;
14470 ma_completion_handler_uwp completionHandler;
14475 HRESULT activateResult;
14476 ma_IUnknown* pActivatedInterface;
14478 MA_ASSERT(pContext != NULL);
14479 MA_ASSERT(ppAudioClient != NULL);
14481 if (pDeviceID != NULL) {
14482 MA_COPY_MEMORY(&iid, pDeviceID->wasapi, sizeof(iid));
14484 if (deviceType == ma_device_type_playback) {
14485 iid = MA_IID_DEVINTERFACE_AUDIO_RENDER;
14487 iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE;
14491 #if defined(__cplusplus)
14492 hr = StringFromIID(iid, &iidStr);
14494 hr = StringFromIID(&iid, &iidStr);
14497 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));
14500 result = ma_completion_handler_uwp_init(&completionHandler);
14501 if (result != MA_SUCCESS) {
14502 ma_CoTaskMemFree(pContext, iidStr);
14503 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", result);
14506 #if defined(__cplusplus)
14507 hr = ActivateAudioInterfaceAsync(iidStr, MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
14509 hr = ActivateAudioInterfaceAsync(iidStr, &MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
14512 ma_completion_handler_uwp_uninit(&completionHandler);
14513 ma_CoTaskMemFree(pContext, iidStr);
14514 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.", ma_result_from_HRESULT(hr));
14517 ma_CoTaskMemFree(pContext, iidStr);
14519 /* Wait for the async operation for finish. */
14520 ma_completion_handler_uwp_wait(&completionHandler);
14521 ma_completion_handler_uwp_uninit(&completionHandler);
14523 hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface);
14524 ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
14526 if (FAILED(hr) || FAILED(activateResult)) {
14527 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));
14530 /* Here is where we grab the IAudioClient interface. */
14531 hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient);
14533 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.", ma_result_from_HRESULT(hr));
14536 if (ppActivatedInterface) {
14537 *ppActivatedInterface = pActivatedInterface;
14539 ma_IUnknown_Release(pActivatedInterface);
14546 static 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)
14548 #ifdef MA_WIN32_DESKTOP
14549 return ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface);
14551 return ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface);
14556 static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
14558 /* Different enumeration for desktop and UWP. */
14559 #ifdef MA_WIN32_DESKTOP
14562 ma_IMMDeviceEnumerator* pDeviceEnumerator;
14564 hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
14566 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr));
14569 ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData);
14570 ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData);
14572 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
14577 The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate
14578 over devices without using MMDevice, I'm restricting devices to defaults.
14580 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/
14583 ma_bool32 cbResult = MA_TRUE;
14587 ma_device_info deviceInfo;
14588 MA_ZERO_OBJECT(&deviceInfo);
14589 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
14590 deviceInfo.isDefault = MA_TRUE;
14591 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
14596 ma_device_info deviceInfo;
14597 MA_ZERO_OBJECT(&deviceInfo);
14598 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
14599 deviceInfo.isDefault = MA_TRUE;
14600 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
14608 static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
14610 #ifdef MA_WIN32_DESKTOP
14612 ma_IMMDevice* pMMDevice = NULL;
14613 LPWSTR pDefaultDeviceID = NULL;
14615 result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice);
14616 if (result != MA_SUCCESS) {
14620 /* We need the default device ID so we can set the isDefault flag in the device info. */
14621 pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType);
14623 result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */
14625 if (pDefaultDeviceID != NULL) {
14626 ma_CoTaskMemFree(pContext, pDefaultDeviceID);
14627 pDefaultDeviceID = NULL;
14630 ma_IMMDevice_Release(pMMDevice);
14634 ma_IAudioClient* pAudioClient;
14637 /* UWP currently only uses default devices. */
14638 if (deviceType == ma_device_type_playback) {
14639 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
14641 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
14644 result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, &pAudioClient, NULL);
14645 if (result != MA_SUCCESS) {
14649 result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo);
14651 pDeviceInfo->isDefault = MA_TRUE; /* UWP only supports default devices. */
14653 ma_IAudioClient_Release(pAudioClient);
14658 static ma_result ma_device_uninit__wasapi(ma_device* pDevice)
14660 MA_ASSERT(pDevice != NULL);
14662 #ifdef MA_WIN32_DESKTOP
14663 if (pDevice->wasapi.pDeviceEnumerator) {
14664 ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient);
14665 ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator);
14669 if (pDevice->wasapi.pRenderClient) {
14670 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
14672 if (pDevice->wasapi.pCaptureClient) {
14673 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
14676 if (pDevice->wasapi.pAudioClientPlayback) {
14677 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
14679 if (pDevice->wasapi.pAudioClientCapture) {
14680 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
14683 if (pDevice->wasapi.hEventPlayback) {
14684 CloseHandle(pDevice->wasapi.hEventPlayback);
14686 if (pDevice->wasapi.hEventCapture) {
14687 CloseHandle(pDevice->wasapi.hEventCapture);
14697 ma_format formatIn;
14698 ma_uint32 channelsIn;
14699 ma_uint32 sampleRateIn;
14700 ma_channel channelMapIn[MA_MAX_CHANNELS];
14701 ma_uint32 periodSizeInFramesIn;
14702 ma_uint32 periodSizeInMillisecondsIn;
14703 ma_uint32 periodsIn;
14704 ma_share_mode shareMode;
14705 ma_performance_profile performanceProfile;
14706 ma_bool32 noAutoConvertSRC;
14707 ma_bool32 noDefaultQualitySRC;
14708 ma_bool32 noHardwareOffloading;
14711 ma_IAudioClient* pAudioClient;
14712 ma_IAudioRenderClient* pRenderClient;
14713 ma_IAudioCaptureClient* pCaptureClient;
14714 ma_format formatOut;
14715 ma_uint32 channelsOut;
14716 ma_uint32 sampleRateOut;
14717 ma_channel channelMapOut[MA_MAX_CHANNELS];
14718 ma_uint32 periodSizeInFramesOut;
14719 ma_uint32 periodsOut;
14720 ma_bool32 usingAudioClient3;
14721 char deviceName[256];
14723 } ma_device_init_internal_data__wasapi;
14725 static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__wasapi* pData)
14728 ma_result result = MA_SUCCESS;
14729 const char* errorMsg = "";
14730 MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
14731 DWORD streamFlags = 0;
14732 MA_REFERENCE_TIME periodDurationInMicroseconds;
14733 ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE;
14734 WAVEFORMATEXTENSIBLE wf;
14735 ma_WASAPIDeviceInterface* pDeviceInterface = NULL;
14736 ma_IAudioClient2* pAudioClient2;
14737 ma_uint32 nativeSampleRate;
14739 MA_ASSERT(pContext != NULL);
14740 MA_ASSERT(pData != NULL);
14742 /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */
14743 if (deviceType == ma_device_type_duplex) {
14744 return MA_INVALID_ARGS;
14747 pData->pAudioClient = NULL;
14748 pData->pRenderClient = NULL;
14749 pData->pCaptureClient = NULL;
14751 streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
14752 if (!pData->noAutoConvertSRC && pData->sampleRateIn != 0 && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */
14753 streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
14755 if (!pData->noDefaultQualitySRC && pData->sampleRateIn != 0 && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) {
14756 streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
14758 if (deviceType == ma_device_type_loopback) {
14759 streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK;
14762 result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, &pData->pAudioClient, &pDeviceInterface);
14763 if (result != MA_SUCCESS) {
14767 MA_ZERO_OBJECT(&wf);
14769 /* Try enabling hardware offloading. */
14770 if (!pData->noHardwareOffloading) {
14771 hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2);
14772 if (SUCCEEDED(hr)) {
14773 BOOL isHardwareOffloadingSupported = 0;
14774 hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported);
14775 if (SUCCEEDED(hr) && isHardwareOffloadingSupported) {
14776 ma_AudioClientProperties clientProperties;
14777 MA_ZERO_OBJECT(&clientProperties);
14778 clientProperties.cbSize = sizeof(clientProperties);
14779 clientProperties.bIsOffload = 1;
14780 clientProperties.eCategory = MA_AudioCategory_Other;
14781 ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties);
14784 pAudioClient2->lpVtbl->Release(pAudioClient2);
14788 /* 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. */
14789 result = MA_FORMAT_NOT_SUPPORTED;
14790 if (pData->shareMode == ma_share_mode_exclusive) {
14791 #ifdef MA_WIN32_DESKTOP
14792 /* In exclusive mode on desktop we always use the backend's native format. */
14793 ma_IPropertyStore* pStore = NULL;
14794 hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore);
14795 if (SUCCEEDED(hr)) {
14797 ma_PropVariantInit(&prop);
14798 hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop);
14799 if (SUCCEEDED(hr)) {
14800 WAVEFORMATEX* pActualFormat = (WAVEFORMATEX*)prop.blob.pBlobData;
14801 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL);
14802 if (SUCCEEDED(hr)) {
14803 MA_COPY_MEMORY(&wf, pActualFormat, sizeof(WAVEFORMATEXTENSIBLE));
14806 ma_PropVariantClear(pContext, &prop);
14809 ma_IPropertyStore_Release(pStore);
14813 I do not know how to query the device's native format on UWP so for now I'm just disabling support for
14814 exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported()
14815 until you find one that works.
14817 TODO: Add support for exclusive mode to UWP.
14823 shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE;
14824 result = MA_SUCCESS;
14826 result = MA_SHARE_MODE_NOT_SUPPORTED;
14829 /* In shared mode we are always using the format reported by the operating system. */
14830 WAVEFORMATEXTENSIBLE* pNativeFormat = NULL;
14831 hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (WAVEFORMATEX**)&pNativeFormat);
14833 result = MA_FORMAT_NOT_SUPPORTED;
14835 MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(wf));
14836 result = MA_SUCCESS;
14839 ma_CoTaskMemFree(pContext, pNativeFormat);
14841 shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
14844 /* Return an error if we still haven't found a format. */
14845 if (result != MA_SUCCESS) {
14846 errorMsg = "[WASAPI] Failed to find best device mix format.";
14851 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
14852 WASAPI to perform the sample rate conversion.
14854 nativeSampleRate = wf.Format.nSamplesPerSec;
14855 if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) {
14856 wf.Format.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE;
14857 wf.Format.nAvgBytesPerSec = wf.Format.nSamplesPerSec * wf.Format.nBlockAlign;
14860 pData->formatOut = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)&wf);
14861 if (pData->formatOut == ma_format_unknown) {
14863 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
14864 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
14865 completeness we'll check for it and return MA_FORMAT_NOT_SUPPORTED.
14867 if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
14868 result = MA_SHARE_MODE_NOT_SUPPORTED;
14870 result = MA_FORMAT_NOT_SUPPORTED;
14873 errorMsg = "[WASAPI] Native format not supported.";
14877 pData->channelsOut = wf.Format.nChannels;
14878 pData->sampleRateOut = wf.Format.nSamplesPerSec;
14880 /* Get the internal channel map based on the channel mask. */
14881 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut);
14884 pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS;
14885 pData->periodSizeInFramesOut = pData->periodSizeInFramesIn;
14886 if (pData->periodSizeInFramesOut == 0) {
14887 if (pData->periodSizeInMillisecondsIn == 0) {
14888 if (pData->performanceProfile == ma_performance_profile_low_latency) {
14889 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.Format.nSamplesPerSec);
14891 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.Format.nSamplesPerSec);
14894 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.Format.nSamplesPerSec);
14898 periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.Format.nSamplesPerSec;
14901 /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */
14902 if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
14903 MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * 10;
14906 If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing
14907 it and trying it again.
14911 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL);
14912 if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) {
14913 if (bufferDuration > 500*10000) {
14916 if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */
14920 bufferDuration = bufferDuration * 2;
14928 if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
14929 ma_uint32 bufferSizeInFrames;
14930 hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
14931 if (SUCCEEDED(hr)) {
14932 bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.Format.nSamplesPerSec * bufferSizeInFrames) + 0.5);
14934 /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */
14935 ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
14937 #ifdef MA_WIN32_DESKTOP
14938 hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient);
14940 hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient);
14943 if (SUCCEEDED(hr)) {
14944 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL);
14950 /* 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. */
14951 if (hr == E_ACCESSDENIED) {
14952 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED;
14953 } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
14954 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY;
14956 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr);
14962 if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) {
14964 Low latency shared mode via IAudioClient3.
14968 Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the
14969 use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using
14970 any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to
14971 that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM.
14973 #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE
14974 if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.Format.nSamplesPerSec) {
14975 ma_IAudioClient3* pAudioClient3 = NULL;
14976 hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3);
14977 if (SUCCEEDED(hr)) {
14978 ma_uint32 defaultPeriodInFrames;
14979 ma_uint32 fundamentalPeriodInFrames;
14980 ma_uint32 minPeriodInFrames;
14981 ma_uint32 maxPeriodInFrames;
14982 hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames);
14983 if (SUCCEEDED(hr)) {
14984 ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut;
14985 ma_uint32 actualPeriodInFrames = desiredPeriodInFrames;
14987 /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */
14988 actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames;
14989 actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames;
14991 /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */
14992 actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames);
14994 #if defined(MA_DEBUG_OUTPUT)
14995 printf("[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames);
14996 printf(" defaultPeriodInFrames=%d\n", defaultPeriodInFrames);
14997 printf(" fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames);
14998 printf(" minPeriodInFrames=%d\n", minPeriodInFrames);
14999 printf(" maxPeriodInFrames=%d\n", maxPeriodInFrames);
15002 /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */
15003 if (actualPeriodInFrames >= desiredPeriodInFrames) {
15005 MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified,
15006 IAudioClient3_InitializeSharedAudioStream() will fail.
15008 hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (WAVEFORMATEX*)&wf, NULL);
15009 if (SUCCEEDED(hr)) {
15010 wasInitializedUsingIAudioClient3 = MA_TRUE;
15011 pData->periodSizeInFramesOut = actualPeriodInFrames;
15012 #if defined(MA_DEBUG_OUTPUT)
15013 printf("[WASAPI] Using IAudioClient3\n");
15014 printf(" periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut);
15017 #if defined(MA_DEBUG_OUTPUT)
15018 printf("[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n");
15022 #if defined(MA_DEBUG_OUTPUT)
15023 printf("[WASAPI] Not using IAudioClient3 because the desired period size is larger than the maximum supported by IAudioClient3.\n");
15027 #if defined(MA_DEBUG_OUTPUT)
15028 printf("[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n");
15032 ma_IAudioClient3_Release(pAudioClient3);
15033 pAudioClient3 = NULL;
15037 #if defined(MA_DEBUG_OUTPUT)
15038 printf("[WASAPI] Not using IAudioClient3 because MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE is enabled.\n");
15042 /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */
15043 if (!wasInitializedUsingIAudioClient3) {
15044 MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */
15045 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL);
15047 if (hr == E_ACCESSDENIED) {
15048 errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED;
15049 } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
15050 errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY;
15052 errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr);
15060 if (!wasInitializedUsingIAudioClient3) {
15061 ma_uint32 bufferSizeInFrames;
15062 hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
15064 errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr);
15068 pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut;
15071 pData->usingAudioClient3 = wasInitializedUsingIAudioClient3;
15074 if (deviceType == ma_device_type_playback) {
15075 result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient);
15077 result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pCaptureClient);
15080 /*if (FAILED(hr)) {*/
15081 if (result != MA_SUCCESS) {
15082 errorMsg = "[WASAPI] Failed to get audio client service.";
15087 /* Grab the name of the device. */
15088 #ifdef MA_WIN32_DESKTOP
15090 ma_IPropertyStore *pProperties;
15091 hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties);
15092 if (SUCCEEDED(hr)) {
15093 PROPVARIANT varName;
15094 ma_PropVariantInit(&varName);
15095 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName);
15096 if (SUCCEEDED(hr)) {
15097 WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE);
15098 ma_PropVariantClear(pContext, &varName);
15101 ma_IPropertyStore_Release(pProperties);
15107 For the WASAPI backend we need to know the actual IDs of the device in order to do automatic
15108 stream routing so that IDs can be compared and we can determine which device has been detached
15109 and whether or not it matches with our ma_device.
15111 #ifdef MA_WIN32_DESKTOP
15114 ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id);
15119 /* TODO: Implement me. Need to figure out how to get the ID of the default device. */
15125 #ifdef MA_WIN32_DESKTOP
15126 if (pDeviceInterface != NULL) {
15127 ma_IMMDevice_Release(pDeviceInterface);
15130 if (pDeviceInterface != NULL) {
15131 ma_IUnknown_Release(pDeviceInterface);
15135 if (result != MA_SUCCESS) {
15136 if (pData->pRenderClient) {
15137 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient);
15138 pData->pRenderClient = NULL;
15140 if (pData->pCaptureClient) {
15141 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient);
15142 pData->pCaptureClient = NULL;
15144 if (pData->pAudioClient) {
15145 ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
15146 pData->pAudioClient = NULL;
15149 if (errorMsg != NULL && errorMsg[0] != '\0') {
15150 ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, errorMsg, result);
15159 static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType)
15161 ma_device_init_internal_data__wasapi data;
15164 MA_ASSERT(pDevice != NULL);
15166 /* We only re-initialize the playback or capture device. Never a full-duplex device. */
15167 if (deviceType == ma_device_type_duplex) {
15168 return MA_INVALID_ARGS;
15173 Before reinitializing the device we need to free the previous audio clients.
15175 There's a known memory leak here. We will be calling this from the routing change callback that
15176 is fired by WASAPI. If we attempt to release the IAudioClient we will deadlock. In my opinion
15177 this is a bug. I'm not sure what I need to do to handle this cleanly, but I think we'll probably
15178 need some system where we post an event, but delay the execution of it until the callback has
15179 returned. I'm not sure how to do this reliably, however. I have set up some infrastructure for
15180 a command thread which might be useful for this.
15182 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
15183 if (pDevice->wasapi.pCaptureClient) {
15184 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
15185 pDevice->wasapi.pCaptureClient = NULL;
15188 if (pDevice->wasapi.pAudioClientCapture) {
15189 /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/
15190 pDevice->wasapi.pAudioClientCapture = NULL;
15194 if (deviceType == ma_device_type_playback) {
15195 if (pDevice->wasapi.pRenderClient) {
15196 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
15197 pDevice->wasapi.pRenderClient = NULL;
15200 if (pDevice->wasapi.pAudioClientPlayback) {
15201 /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/
15202 pDevice->wasapi.pAudioClientPlayback = NULL;
15207 if (deviceType == ma_device_type_playback) {
15208 data.formatIn = pDevice->playback.format;
15209 data.channelsIn = pDevice->playback.channels;
15210 MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
15211 data.shareMode = pDevice->playback.shareMode;
15213 data.formatIn = pDevice->capture.format;
15214 data.channelsIn = pDevice->capture.channels;
15215 MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
15216 data.shareMode = pDevice->capture.shareMode;
15219 data.sampleRateIn = pDevice->sampleRate;
15220 data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames;
15221 data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds;
15222 data.periodsIn = pDevice->wasapi.originalPeriods;
15223 data.performanceProfile = pDevice->wasapi.originalPerformanceProfile;
15224 data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC;
15225 data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC;
15226 data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading;
15227 result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data);
15228 if (result != MA_SUCCESS) {
15232 /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */
15233 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
15234 pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
15235 pDevice->wasapi.pCaptureClient = data.pCaptureClient;
15237 pDevice->capture.internalFormat = data.formatOut;
15238 pDevice->capture.internalChannels = data.channelsOut;
15239 pDevice->capture.internalSampleRate = data.sampleRateOut;
15240 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
15241 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
15242 pDevice->capture.internalPeriods = data.periodsOut;
15243 ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName);
15245 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture);
15247 pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
15248 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualPeriodSizeInFramesCapture);
15250 /* We must always have a valid ID. */
15251 ma_wcscpy_s(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi);
15254 if (deviceType == ma_device_type_playback) {
15255 pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
15256 pDevice->wasapi.pRenderClient = data.pRenderClient;
15258 pDevice->playback.internalFormat = data.formatOut;
15259 pDevice->playback.internalChannels = data.channelsOut;
15260 pDevice->playback.internalSampleRate = data.sampleRateOut;
15261 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
15262 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
15263 pDevice->playback.internalPeriods = data.periodsOut;
15264 ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName);
15266 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback);
15268 pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
15269 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualPeriodSizeInFramesPlayback);
15271 /* We must always have a valid ID. */
15272 ma_wcscpy_s(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi);
15278 static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
15280 ma_result result = MA_SUCCESS;
15282 #ifdef MA_WIN32_DESKTOP
15284 ma_IMMDeviceEnumerator* pDeviceEnumerator;
15287 MA_ASSERT(pDevice != NULL);
15289 MA_ZERO_OBJECT(&pDevice->wasapi);
15290 pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
15291 pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
15292 pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
15294 /* Exclusive mode is not allowed with loopback. */
15295 if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) {
15296 return MA_INVALID_DEVICE_CONFIG;
15299 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
15300 ma_device_init_internal_data__wasapi data;
15301 data.formatIn = pDescriptorCapture->format;
15302 data.channelsIn = pDescriptorCapture->channels;
15303 data.sampleRateIn = pDescriptorCapture->sampleRate;
15304 MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
15305 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
15306 data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds;
15307 data.periodsIn = pDescriptorCapture->periodCount;
15308 data.shareMode = pDescriptorCapture->shareMode;
15309 data.performanceProfile = pConfig->performanceProfile;
15310 data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
15311 data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
15312 data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
15314 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);
15315 if (result != MA_SUCCESS) {
15319 pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
15320 pDevice->wasapi.pCaptureClient = data.pCaptureClient;
15321 pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;
15322 pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
15323 pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount;
15324 pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile;
15327 The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled,
15328 however, because we want to block until we actually have something for the first call to ma_device_read().
15330 pDevice->wasapi.hEventCapture = CreateEventW(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */
15331 if (pDevice->wasapi.hEventCapture == NULL) {
15332 result = ma_result_from_GetLastError(GetLastError());
15334 if (pDevice->wasapi.pCaptureClient != NULL) {
15335 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
15336 pDevice->wasapi.pCaptureClient = NULL;
15338 if (pDevice->wasapi.pAudioClientCapture != NULL) {
15339 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
15340 pDevice->wasapi.pAudioClientCapture = NULL;
15343 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture.", result);
15345 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture);
15347 pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
15348 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualPeriodSizeInFramesCapture);
15350 /* We must always have a valid ID. */
15351 ma_wcscpy_s(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi);
15353 /* The descriptor needs to be updated with actual values. */
15354 pDescriptorCapture->format = data.formatOut;
15355 pDescriptorCapture->channels = data.channelsOut;
15356 pDescriptorCapture->sampleRate = data.sampleRateOut;
15357 MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
15358 pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut;
15359 pDescriptorCapture->periodCount = data.periodsOut;
15362 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
15363 ma_device_init_internal_data__wasapi data;
15364 data.formatIn = pDescriptorPlayback->format;
15365 data.channelsIn = pDescriptorPlayback->channels;
15366 data.sampleRateIn = pDescriptorPlayback->sampleRate;
15367 MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
15368 data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames;
15369 data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;
15370 data.periodsIn = pDescriptorPlayback->periodCount;
15371 data.shareMode = pDescriptorPlayback->shareMode;
15372 data.performanceProfile = pConfig->performanceProfile;
15373 data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
15374 data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
15375 data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
15377 result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data);
15378 if (result != MA_SUCCESS) {
15379 if (pConfig->deviceType == ma_device_type_duplex) {
15380 if (pDevice->wasapi.pCaptureClient != NULL) {
15381 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
15382 pDevice->wasapi.pCaptureClient = NULL;
15384 if (pDevice->wasapi.pAudioClientCapture != NULL) {
15385 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
15386 pDevice->wasapi.pAudioClientCapture = NULL;
15389 CloseHandle(pDevice->wasapi.hEventCapture);
15390 pDevice->wasapi.hEventCapture = NULL;
15395 pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
15396 pDevice->wasapi.pRenderClient = data.pRenderClient;
15397 pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;
15398 pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
15399 pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount;
15400 pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile;
15403 The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled
15404 only after the whole available space has been filled, never before.
15406 The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able
15407 to get passed WaitForMultipleObjects().
15409 pDevice->wasapi.hEventPlayback = CreateEventW(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */
15410 if (pDevice->wasapi.hEventPlayback == NULL) {
15411 result = ma_result_from_GetLastError(GetLastError());
15413 if (pConfig->deviceType == ma_device_type_duplex) {
15414 if (pDevice->wasapi.pCaptureClient != NULL) {
15415 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
15416 pDevice->wasapi.pCaptureClient = NULL;
15418 if (pDevice->wasapi.pAudioClientCapture != NULL) {
15419 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
15420 pDevice->wasapi.pAudioClientCapture = NULL;
15423 CloseHandle(pDevice->wasapi.hEventCapture);
15424 pDevice->wasapi.hEventCapture = NULL;
15427 if (pDevice->wasapi.pRenderClient != NULL) {
15428 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
15429 pDevice->wasapi.pRenderClient = NULL;
15431 if (pDevice->wasapi.pAudioClientPlayback != NULL) {
15432 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
15433 pDevice->wasapi.pAudioClientPlayback = NULL;
15436 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback.", result);
15438 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback);
15440 pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
15441 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualPeriodSizeInFramesPlayback);
15443 /* We must always have a valid ID. */
15444 ma_wcscpy_s(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi);
15446 /* The descriptor needs to be updated with actual values. */
15447 pDescriptorPlayback->format = data.formatOut;
15448 pDescriptorPlayback->channels = data.channelsOut;
15449 pDescriptorPlayback->sampleRate = data.sampleRateOut;
15450 MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
15451 pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut;
15452 pDescriptorPlayback->periodCount = data.periodsOut;
15456 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
15457 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
15458 stop the device outright and let the application handle it.
15460 #ifdef MA_WIN32_DESKTOP
15461 if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) {
15462 if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID == NULL) {
15463 pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE;
15465 if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) {
15466 pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE;
15470 hr = ma_CoCreateInstance(pDevice->pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
15472 ma_device_uninit__wasapi(pDevice);
15473 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr));
15476 pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl;
15477 pDevice->wasapi.notificationClient.counter = 1;
15478 pDevice->wasapi.notificationClient.pDevice = pDevice;
15480 hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient);
15481 if (SUCCEEDED(hr)) {
15482 pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator;
15484 /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */
15485 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
15489 c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE);
15490 c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
15495 static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount)
15497 ma_uint32 paddingFramesCount;
15499 ma_share_mode shareMode;
15501 MA_ASSERT(pDevice != NULL);
15502 MA_ASSERT(pFrameCount != NULL);
15506 if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) {
15507 return MA_INVALID_OPERATION;
15511 I've had a report that GetCurrentPadding() is returning a frame count of 0 which is preventing
15512 higher level function calls from doing anything because it thinks nothing is available. I have
15513 taken a look at the documentation and it looks like this is unnecessary in exclusive mode.
15515 From Microsoft's documentation:
15517 For an exclusive-mode rendering or capture stream that was initialized with the
15518 AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag, the client typically has no use for the padding
15519 value reported by GetCurrentPadding. Instead, the client accesses an entire buffer during
15520 each processing pass.
15522 Considering this, I'm going to skip GetCurrentPadding() for exclusive mode and just report the
15523 entire buffer. This depends on the caller making sure they wait on the event handler.
15525 shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode;
15526 if (shareMode == ma_share_mode_shared) {
15528 hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount);
15530 return ma_result_from_HRESULT(hr);
15533 if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
15534 *pFrameCount = pDevice->wasapi.actualPeriodSizeInFramesPlayback - paddingFramesCount;
15536 *pFrameCount = paddingFramesCount;
15539 /* Exclusive mode. */
15540 if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
15541 *pFrameCount = pDevice->wasapi.actualPeriodSizeInFramesPlayback;
15543 *pFrameCount = pDevice->wasapi.actualPeriodSizeInFramesCapture;
15551 static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType)
15555 if (deviceType == ma_device_type_duplex) {
15556 return MA_INVALID_ARGS;
15559 #ifdef MA_DEBUG_OUTPUT
15560 printf("=== CHANGING DEVICE ===\n");
15563 result = ma_device_reinit__wasapi(pDevice, deviceType);
15564 if (result != MA_SUCCESS) {
15565 #ifdef MA_DEBUG_OUTPUT
15566 printf("[WASAPI] Reinitializing device after route change failed.\n");
15571 ma_device__post_init_setup(pDevice, deviceType);
15576 static ma_result ma_device_start__wasapi(ma_device* pDevice)
15580 MA_ASSERT(pDevice != NULL);
15582 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
15583 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
15585 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device.", ma_result_from_HRESULT(hr));
15588 c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_TRUE);
15591 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
15592 /* No need to do anything for playback as that'll be started automatically in the data loop. */
15598 static ma_result ma_device_stop__wasapi(ma_device* pDevice)
15603 MA_ASSERT(pDevice != NULL);
15605 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
15606 hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
15608 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device.", ma_result_from_HRESULT(hr));
15611 /* The audio client needs to be reset otherwise restarting will fail. */
15612 hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
15614 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device.", ma_result_from_HRESULT(hr));
15617 c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE);
15620 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
15622 The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to
15623 the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played.
15625 if (c89atomic_load_32(&pDevice->wasapi.isStartedPlayback)) {
15626 /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */
15627 DWORD waitTime = pDevice->wasapi.actualPeriodSizeInFramesPlayback / pDevice->playback.internalSampleRate;
15629 if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
15630 WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime);
15632 ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1;
15633 ma_uint32 framesAvailablePlayback;
15635 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
15636 if (result != MA_SUCCESS) {
15640 if (framesAvailablePlayback >= pDevice->wasapi.actualPeriodSizeInFramesPlayback) {
15645 Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames
15646 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.
15648 if (framesAvailablePlayback == prevFramesAvaialablePlayback) {
15651 prevFramesAvaialablePlayback = framesAvailablePlayback;
15653 WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime);
15654 ResetEvent(pDevice->wasapi.hEventPlayback); /* Manual reset. */
15659 hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
15661 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device.", ma_result_from_HRESULT(hr));
15664 /* The audio client needs to be reset otherwise restarting will fail. */
15665 hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
15667 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device.", ma_result_from_HRESULT(hr));
15670 c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
15677 #ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS
15678 #define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000
15681 static ma_result ma_device_data_loop__wasapi(ma_device* pDevice)
15685 ma_bool32 exitLoop = MA_FALSE;
15686 ma_uint32 framesWrittenToPlaybackDevice = 0;
15687 ma_uint32 mappedDeviceBufferSizeInFramesCapture = 0;
15688 ma_uint32 mappedDeviceBufferSizeInFramesPlayback = 0;
15689 ma_uint32 mappedDeviceBufferFramesRemainingCapture = 0;
15690 ma_uint32 mappedDeviceBufferFramesRemainingPlayback = 0;
15691 BYTE* pMappedDeviceBufferCapture = NULL;
15692 BYTE* pMappedDeviceBufferPlayback = NULL;
15693 ma_uint32 bpfCaptureDevice = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
15694 ma_uint32 bpfPlaybackDevice = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
15695 ma_uint32 bpfCaptureClient = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
15696 ma_uint32 bpfPlaybackClient = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
15697 ma_uint8 inputDataInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
15698 ma_uint32 inputDataInClientFormatCap = 0;
15699 ma_uint8 outputDataInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
15700 ma_uint32 outputDataInClientFormatCap = 0;
15701 ma_uint32 outputDataInClientFormatCount = 0;
15702 ma_uint32 outputDataInClientFormatConsumed = 0;
15703 ma_uint32 periodSizeInFramesCapture = 0;
15705 MA_ASSERT(pDevice != NULL);
15707 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
15708 periodSizeInFramesCapture = pDevice->capture.internalPeriodSizeInFrames;
15709 inputDataInClientFormatCap = sizeof(inputDataInClientFormat) / bpfCaptureClient;
15712 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
15713 outputDataInClientFormatCap = sizeof(outputDataInClientFormat) / bpfPlaybackClient;
15716 while (ma_device_get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
15717 switch (pDevice->type)
15719 case ma_device_type_duplex:
15721 ma_uint32 framesAvailableCapture;
15722 ma_uint32 framesAvailablePlayback;
15723 DWORD flagsCapture; /* Passed to IAudioCaptureClient_GetBuffer(). */
15725 /* The process is to map the playback buffer and fill it as quickly as possible from input data. */
15726 if (pMappedDeviceBufferPlayback == NULL) {
15727 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
15728 if (result != MA_SUCCESS) {
15732 /*printf("TRACE 1: framesAvailablePlayback=%d\n", framesAvailablePlayback);*/
15735 /* In exclusive mode, the frame count needs to exactly match the value returned by GetCurrentPadding(). */
15736 if (pDevice->playback.shareMode != ma_share_mode_exclusive) {
15737 if (framesAvailablePlayback > pDevice->wasapi.periodSizeInFramesPlayback) {
15738 framesAvailablePlayback = pDevice->wasapi.periodSizeInFramesPlayback;
15742 /* We're ready to map the playback device's buffer. We don't release this until it's been entirely filled. */
15743 hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, &pMappedDeviceBufferPlayback);
15745 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));
15746 exitLoop = MA_TRUE;
15750 mappedDeviceBufferSizeInFramesPlayback = framesAvailablePlayback;
15751 mappedDeviceBufferFramesRemainingPlayback = framesAvailablePlayback;
15754 if (mappedDeviceBufferFramesRemainingPlayback > 0) {
15755 /* At this point we should have a buffer available for output. We need to keep writing input samples to it. */
15757 /* Try grabbing some captured data if we haven't already got a mapped buffer. */
15758 if (pMappedDeviceBufferCapture == NULL) {
15759 if (pDevice->capture.shareMode == ma_share_mode_shared) {
15760 if (WaitForSingleObject(pDevice->wasapi.hEventCapture, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
15761 return MA_ERROR; /* Wait failed. */
15765 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture);
15766 if (result != MA_SUCCESS) {
15767 exitLoop = MA_TRUE;
15771 /*printf("TRACE 2: framesAvailableCapture=%d\n", framesAvailableCapture);*/
15773 /* Wait for more if nothing is available. */
15774 if (framesAvailableCapture == 0) {
15775 /* In exclusive mode we waited at the top. */
15776 if (pDevice->capture.shareMode != ma_share_mode_shared) {
15777 if (WaitForSingleObject(pDevice->wasapi.hEventCapture, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
15778 return MA_ERROR; /* Wait failed. */
15785 /* Getting here means there's data available for writing to the output device. */
15786 mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture);
15787 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
15789 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));
15790 exitLoop = MA_TRUE;
15795 /* Overrun detection. */
15796 if ((flagsCapture & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
15797 /* Glitched. Probably due to an overrun. */
15798 #ifdef MA_DEBUG_OUTPUT
15799 printf("[WASAPI] Data discontinuity (possible overrun). framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
15803 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
15804 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
15807 if (framesAvailableCapture >= pDevice->wasapi.actualPeriodSizeInFramesCapture) {
15808 #ifdef MA_DEBUG_OUTPUT
15809 printf("[WASAPI] Synchronizing capture stream. ");
15813 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
15818 framesAvailableCapture -= mappedDeviceBufferSizeInFramesCapture;
15820 if (framesAvailableCapture > 0) {
15821 mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture);
15822 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
15824 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));
15825 exitLoop = MA_TRUE;
15829 pMappedDeviceBufferCapture = NULL;
15830 mappedDeviceBufferSizeInFramesCapture = 0;
15832 } while (framesAvailableCapture > periodSizeInFramesCapture);
15833 #ifdef MA_DEBUG_OUTPUT
15834 printf("framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
15838 #ifdef MA_DEBUG_OUTPUT
15839 if (flagsCapture != 0) {
15840 printf("[WASAPI] Capture Flags: %ld\n", flagsCapture);
15845 mappedDeviceBufferFramesRemainingCapture = mappedDeviceBufferSizeInFramesCapture;
15849 /* 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. */
15851 BYTE* pRunningDeviceBufferCapture;
15852 BYTE* pRunningDeviceBufferPlayback;
15853 ma_uint32 framesToProcess;
15854 ma_uint32 framesProcessed;
15856 pRunningDeviceBufferCapture = pMappedDeviceBufferCapture + ((mappedDeviceBufferSizeInFramesCapture - mappedDeviceBufferFramesRemainingCapture ) * bpfCaptureDevice);
15857 pRunningDeviceBufferPlayback = pMappedDeviceBufferPlayback + ((mappedDeviceBufferSizeInFramesPlayback - mappedDeviceBufferFramesRemainingPlayback) * bpfPlaybackDevice);
15859 /* There may be some data sitting in the converter that needs to be processed first. Once this is exhaused, run the data callback again. */
15860 if (!pDevice->playback.converter.isPassthrough && outputDataInClientFormatConsumed < outputDataInClientFormatCount) {
15861 ma_uint64 convertedFrameCountClient = (outputDataInClientFormatCount - outputDataInClientFormatConsumed);
15862 ma_uint64 convertedFrameCountDevice = mappedDeviceBufferFramesRemainingPlayback;
15863 void* pConvertedFramesClient = outputDataInClientFormat + (outputDataInClientFormatConsumed * bpfPlaybackClient);
15864 void* pConvertedFramesDevice = pRunningDeviceBufferPlayback;
15865 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesClient, &convertedFrameCountClient, pConvertedFramesDevice, &convertedFrameCountDevice);
15866 if (result != MA_SUCCESS) {
15870 outputDataInClientFormatConsumed += (ma_uint32)convertedFrameCountClient; /* Safe cast. */
15871 mappedDeviceBufferFramesRemainingPlayback -= (ma_uint32)convertedFrameCountDevice; /* Safe cast. */
15873 if (mappedDeviceBufferFramesRemainingPlayback == 0) {
15879 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
15880 buffers directly to the callback.
15882 if (pDevice->capture.converter.isPassthrough && pDevice->playback.converter.isPassthrough) {
15883 /* Optimal path. We can pass mapped pointers directly to the callback. */
15884 framesToProcess = ma_min(mappedDeviceBufferFramesRemainingCapture, mappedDeviceBufferFramesRemainingPlayback);
15885 framesProcessed = framesToProcess;
15887 ma_device__on_data(pDevice, pRunningDeviceBufferPlayback, pRunningDeviceBufferCapture, framesToProcess);
15889 mappedDeviceBufferFramesRemainingCapture -= framesProcessed;
15890 mappedDeviceBufferFramesRemainingPlayback -= framesProcessed;
15892 if (mappedDeviceBufferFramesRemainingCapture == 0) {
15893 break; /* Exhausted input data. */
15895 if (mappedDeviceBufferFramesRemainingPlayback == 0) {
15896 break; /* Exhausted output data. */
15898 } else if (pDevice->capture.converter.isPassthrough) {
15899 /* The input buffer is a passthrough, but the playback buffer requires a conversion. */
15900 framesToProcess = ma_min(mappedDeviceBufferFramesRemainingCapture, outputDataInClientFormatCap);
15901 framesProcessed = framesToProcess;
15903 ma_device__on_data(pDevice, outputDataInClientFormat, pRunningDeviceBufferCapture, framesToProcess);
15904 outputDataInClientFormatCount = framesProcessed;
15905 outputDataInClientFormatConsumed = 0;
15907 mappedDeviceBufferFramesRemainingCapture -= framesProcessed;
15908 if (mappedDeviceBufferFramesRemainingCapture == 0) {
15909 break; /* Exhausted input data. */
15911 } else if (pDevice->playback.converter.isPassthrough) {
15912 /* The input buffer requires conversion, the playback buffer is passthrough. */
15913 ma_uint64 capturedDeviceFramesToProcess = mappedDeviceBufferFramesRemainingCapture;
15914 ma_uint64 capturedClientFramesToProcess = ma_min(inputDataInClientFormatCap, mappedDeviceBufferFramesRemainingPlayback);
15916 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningDeviceBufferCapture, &capturedDeviceFramesToProcess, inputDataInClientFormat, &capturedClientFramesToProcess);
15917 if (result != MA_SUCCESS) {
15921 if (capturedClientFramesToProcess == 0) {
15925 ma_device__on_data(pDevice, pRunningDeviceBufferPlayback, inputDataInClientFormat, (ma_uint32)capturedClientFramesToProcess); /* Safe cast. */
15927 mappedDeviceBufferFramesRemainingCapture -= (ma_uint32)capturedDeviceFramesToProcess;
15928 mappedDeviceBufferFramesRemainingPlayback -= (ma_uint32)capturedClientFramesToProcess;
15930 ma_uint64 capturedDeviceFramesToProcess = mappedDeviceBufferFramesRemainingCapture;
15931 ma_uint64 capturedClientFramesToProcess = ma_min(inputDataInClientFormatCap, outputDataInClientFormatCap);
15933 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningDeviceBufferCapture, &capturedDeviceFramesToProcess, inputDataInClientFormat, &capturedClientFramesToProcess);
15934 if (result != MA_SUCCESS) {
15938 if (capturedClientFramesToProcess == 0) {
15942 ma_device__on_data(pDevice, outputDataInClientFormat, inputDataInClientFormat, (ma_uint32)capturedClientFramesToProcess);
15944 mappedDeviceBufferFramesRemainingCapture -= (ma_uint32)capturedDeviceFramesToProcess;
15945 outputDataInClientFormatCount = (ma_uint32)capturedClientFramesToProcess;
15946 outputDataInClientFormatConsumed = 0;
15951 /* If at this point we've run out of capture data we need to release the buffer. */
15952 if (mappedDeviceBufferFramesRemainingCapture == 0 && pMappedDeviceBufferCapture != NULL) {
15953 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
15955 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));
15956 exitLoop = MA_TRUE;
15960 /*printf("TRACE: Released capture buffer\n");*/
15962 pMappedDeviceBufferCapture = NULL;
15963 mappedDeviceBufferFramesRemainingCapture = 0;
15964 mappedDeviceBufferSizeInFramesCapture = 0;
15967 /* Get out of this loop if we're run out of room in the playback buffer. */
15968 if (mappedDeviceBufferFramesRemainingPlayback == 0) {
15975 /* If at this point we've run out of data we need to release the buffer. */
15976 if (mappedDeviceBufferFramesRemainingPlayback == 0 && pMappedDeviceBufferPlayback != NULL) {
15977 hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, mappedDeviceBufferSizeInFramesPlayback, 0);
15979 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));
15980 exitLoop = MA_TRUE;
15984 /*printf("TRACE: Released playback buffer\n");*/
15985 framesWrittenToPlaybackDevice += mappedDeviceBufferSizeInFramesPlayback;
15987 pMappedDeviceBufferPlayback = NULL;
15988 mappedDeviceBufferFramesRemainingPlayback = 0;
15989 mappedDeviceBufferSizeInFramesPlayback = 0;
15992 if (!c89atomic_load_32(&pDevice->wasapi.isStartedPlayback)) {
15993 ma_uint32 startThreshold = pDevice->playback.internalPeriodSizeInFrames * 1;
15995 /* 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. */
15996 if (startThreshold > pDevice->wasapi.actualPeriodSizeInFramesPlayback) {
15997 startThreshold = pDevice->wasapi.actualPeriodSizeInFramesPlayback;
16000 if (pDevice->playback.shareMode == ma_share_mode_exclusive || framesWrittenToPlaybackDevice >= startThreshold) {
16001 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
16003 ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
16004 ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
16005 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", ma_result_from_HRESULT(hr));
16008 c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
16012 /* Make sure the device has started before waiting. */
16013 if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
16014 return MA_ERROR; /* Wait failed. */
16020 case ma_device_type_capture:
16021 case ma_device_type_loopback:
16023 ma_uint32 framesAvailableCapture;
16024 DWORD flagsCapture; /* Passed to IAudioCaptureClient_GetBuffer(). */
16026 /* Wait for data to become available first. */
16027 if (WaitForSingleObject(pDevice->wasapi.hEventCapture, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
16028 exitLoop = MA_TRUE;
16029 break; /* Wait failed. */
16032 /* 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. */
16033 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture);
16034 if (result != MA_SUCCESS) {
16035 exitLoop = MA_TRUE;
16039 if (framesAvailableCapture < pDevice->wasapi.periodSizeInFramesCapture) {
16040 continue; /* Nothing available. Keep waiting. */
16043 /* Map the data buffer in preparation for sending to the client. */
16044 mappedDeviceBufferSizeInFramesCapture = framesAvailableCapture;
16045 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
16047 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));
16048 exitLoop = MA_TRUE;
16052 /* Overrun detection. */
16053 if ((flagsCapture & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
16054 /* Glitched. Probably due to an overrun. */
16055 #ifdef MA_DEBUG_OUTPUT
16056 printf("[WASAPI] Data discontinuity (possible overrun). framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
16060 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
16061 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
16064 if (framesAvailableCapture >= pDevice->wasapi.actualPeriodSizeInFramesCapture) {
16065 #ifdef MA_DEBUG_OUTPUT
16066 printf("[WASAPI] Synchronizing capture stream. ");
16070 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
16075 framesAvailableCapture -= mappedDeviceBufferSizeInFramesCapture;
16077 if (framesAvailableCapture > 0) {
16078 mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture);
16079 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
16081 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));
16082 exitLoop = MA_TRUE;
16086 pMappedDeviceBufferCapture = NULL;
16087 mappedDeviceBufferSizeInFramesCapture = 0;
16089 } while (framesAvailableCapture > periodSizeInFramesCapture);
16090 #ifdef MA_DEBUG_OUTPUT
16091 printf("framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
16095 #ifdef MA_DEBUG_OUTPUT
16096 if (flagsCapture != 0) {
16097 printf("[WASAPI] Capture Flags: %ld\n", flagsCapture);
16102 /* We should have a buffer at this point, but let's just do a sanity check anyway. */
16103 if (mappedDeviceBufferSizeInFramesCapture > 0 && pMappedDeviceBufferCapture != NULL) {
16104 ma_device__send_frames_to_client(pDevice, mappedDeviceBufferSizeInFramesCapture, pMappedDeviceBufferCapture);
16106 /* At this point we're done with the buffer. */
16107 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
16108 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. */
16109 mappedDeviceBufferSizeInFramesCapture = 0;
16111 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));
16112 exitLoop = MA_TRUE;
16120 case ma_device_type_playback:
16122 ma_uint32 framesAvailablePlayback;
16124 /* Check how much space is available. If this returns 0 we just keep waiting. */
16125 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
16126 if (result != MA_SUCCESS) {
16127 exitLoop = MA_TRUE;
16131 if (framesAvailablePlayback >= pDevice->wasapi.periodSizeInFramesPlayback) {
16132 /* Map a the data buffer in preparation for the callback. */
16133 hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, &pMappedDeviceBufferPlayback);
16135 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));
16136 exitLoop = MA_TRUE;
16140 /* We should have a buffer at this point. */
16141 ma_device__read_frames_from_client(pDevice, framesAvailablePlayback, pMappedDeviceBufferPlayback);
16143 /* At this point we're done writing to the device and we just need to release the buffer. */
16144 hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, 0);
16145 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. */
16146 mappedDeviceBufferSizeInFramesPlayback = 0;
16149 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));
16150 exitLoop = MA_TRUE;
16154 framesWrittenToPlaybackDevice += framesAvailablePlayback;
16157 if (!c89atomic_load_32(&pDevice->wasapi.isStartedPlayback)) {
16158 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
16160 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", ma_result_from_HRESULT(hr));
16161 exitLoop = MA_TRUE;
16165 c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
16168 /* Make sure we don't wait on the event before we've started the device or we may end up deadlocking. */
16169 if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
16170 exitLoop = MA_TRUE;
16171 break; /* Wait failed. Probably timed out. */
16175 default: return MA_INVALID_ARGS;
16179 /* Here is where the device needs to be stopped. */
16180 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
16181 /* Any mapped buffers need to be released. */
16182 if (pMappedDeviceBufferCapture != NULL) {
16183 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
16187 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
16188 /* Any mapped buffers need to be released. */
16189 if (pMappedDeviceBufferPlayback != NULL) {
16190 hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, mappedDeviceBufferSizeInFramesPlayback, 0);
16197 static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice)
16199 MA_ASSERT(pDevice != NULL);
16201 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
16202 SetEvent((HANDLE)pDevice->wasapi.hEventCapture);
16205 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
16206 SetEvent((HANDLE)pDevice->wasapi.hEventPlayback);
16213 static ma_result ma_context_uninit__wasapi(ma_context* pContext)
16215 MA_ASSERT(pContext != NULL);
16216 MA_ASSERT(pContext->backend == ma_backend_wasapi);
16218 if (pContext->wasapi.commandThread != NULL) {
16219 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI);
16220 ma_context_post_command__wasapi(pContext, &cmd);
16221 ma_thread_wait(&pContext->wasapi.commandThread);
16223 /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */
16224 ma_semaphore_uninit(&pContext->wasapi.commandSem);
16225 ma_mutex_uninit(&pContext->wasapi.commandLock);
16231 static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
16233 ma_result result = MA_SUCCESS;
16235 MA_ASSERT(pContext != NULL);
16239 #ifdef MA_WIN32_DESKTOP
16241 WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven
16242 exclusive mode does not work until SP1.
16244 Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error.
16247 ma_OSVERSIONINFOEXW osvi;
16248 ma_handle kernel32DLL;
16249 ma_PFNVerifyVersionInfoW _VerifyVersionInfoW;
16250 ma_PFNVerSetConditionMask _VerSetConditionMask;
16252 kernel32DLL = ma_dlopen(pContext, "kernel32.dll");
16253 if (kernel32DLL == NULL) {
16254 return MA_NO_BACKEND;
16257 _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(pContext, kernel32DLL, "VerifyVersionInfoW");
16258 _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(pContext, kernel32DLL, "VerSetConditionMask");
16259 if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) {
16260 ma_dlclose(pContext, kernel32DLL);
16261 return MA_NO_BACKEND;
16264 MA_ZERO_OBJECT(&osvi);
16265 osvi.dwOSVersionInfoSize = sizeof(osvi);
16266 osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF);
16267 osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF);
16268 osvi.wServicePackMajor = 1;
16269 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))) {
16270 result = MA_SUCCESS;
16272 result = MA_NO_BACKEND;
16275 ma_dlclose(pContext, kernel32DLL);
16279 if (result != MA_SUCCESS) {
16283 MA_ZERO_OBJECT(&pContext->wasapi);
16286 Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread
16287 than the one that retrieved it with GetService(). This can result in a deadlock in two
16290 1) When calling ma_device_uninit() from a different thread to ma_device_init(); and
16291 2) When uninitializing and reinitializing the internal IAudioClient object in response to
16292 automatic stream routing.
16294 We could define ma_device_uninit() such that it must be called on the same thread as
16295 ma_device_init(). We could also just not release the IAudioClient when performing automatic
16296 stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so
16297 we're going to have to work around this with a worker thread. This is not ideal, but I can't
16298 think of a better way to do this.
16300 More information about this can be found here:
16302 https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient
16306 When releasing an IAudioRenderClient interface instance, the client must call the interface's
16307 Release method from the same thread as the call to IAudioClient::GetService that created the
16311 result = ma_mutex_init(&pContext->wasapi.commandLock);
16312 if (result != MA_SUCCESS) {
16316 result = ma_semaphore_init(0, &pContext->wasapi.commandSem);
16317 if (result != MA_SUCCESS) {
16318 ma_mutex_uninit(&pContext->wasapi.commandLock);
16322 result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext);
16323 if (result != MA_SUCCESS) {
16324 ma_semaphore_uninit(&pContext->wasapi.commandSem);
16325 ma_mutex_uninit(&pContext->wasapi.commandLock);
16331 pCallbacks->onContextInit = ma_context_init__wasapi;
16332 pCallbacks->onContextUninit = ma_context_uninit__wasapi;
16333 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi;
16334 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi;
16335 pCallbacks->onDeviceInit = ma_device_init__wasapi;
16336 pCallbacks->onDeviceUninit = ma_device_uninit__wasapi;
16337 pCallbacks->onDeviceStart = ma_device_start__wasapi;
16338 pCallbacks->onDeviceStop = ma_device_stop__wasapi;
16339 pCallbacks->onDeviceRead = NULL; /* Not used. Reading is done manually in the audio thread. */
16340 pCallbacks->onDeviceWrite = NULL; /* Not used. Writing is done manually in the audio thread. */
16341 pCallbacks->onDeviceDataLoop = ma_device_data_loop__wasapi;
16342 pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi;
16348 /******************************************************************************
16350 DirectSound Backend
16352 ******************************************************************************/
16353 #ifdef MA_HAS_DSOUND
16354 /*#include <dsound.h>*/
16356 /*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/
16358 /* miniaudio only uses priority or exclusive modes. */
16359 #define MA_DSSCL_NORMAL 1
16360 #define MA_DSSCL_PRIORITY 2
16361 #define MA_DSSCL_EXCLUSIVE 3
16362 #define MA_DSSCL_WRITEPRIMARY 4
16364 #define MA_DSCAPS_PRIMARYMONO 0x00000001
16365 #define MA_DSCAPS_PRIMARYSTEREO 0x00000002
16366 #define MA_DSCAPS_PRIMARY8BIT 0x00000004
16367 #define MA_DSCAPS_PRIMARY16BIT 0x00000008
16368 #define MA_DSCAPS_CONTINUOUSRATE 0x00000010
16369 #define MA_DSCAPS_EMULDRIVER 0x00000020
16370 #define MA_DSCAPS_CERTIFIED 0x00000040
16371 #define MA_DSCAPS_SECONDARYMONO 0x00000100
16372 #define MA_DSCAPS_SECONDARYSTEREO 0x00000200
16373 #define MA_DSCAPS_SECONDARY8BIT 0x00000400
16374 #define MA_DSCAPS_SECONDARY16BIT 0x00000800
16376 #define MA_DSBCAPS_PRIMARYBUFFER 0x00000001
16377 #define MA_DSBCAPS_STATIC 0x00000002
16378 #define MA_DSBCAPS_LOCHARDWARE 0x00000004
16379 #define MA_DSBCAPS_LOCSOFTWARE 0x00000008
16380 #define MA_DSBCAPS_CTRL3D 0x00000010
16381 #define MA_DSBCAPS_CTRLFREQUENCY 0x00000020
16382 #define MA_DSBCAPS_CTRLPAN 0x00000040
16383 #define MA_DSBCAPS_CTRLVOLUME 0x00000080
16384 #define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100
16385 #define MA_DSBCAPS_CTRLFX 0x00000200
16386 #define MA_DSBCAPS_STICKYFOCUS 0x00004000
16387 #define MA_DSBCAPS_GLOBALFOCUS 0x00008000
16388 #define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000
16389 #define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000
16390 #define MA_DSBCAPS_LOCDEFER 0x00040000
16391 #define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000
16393 #define MA_DSBPLAY_LOOPING 0x00000001
16394 #define MA_DSBPLAY_LOCHARDWARE 0x00000002
16395 #define MA_DSBPLAY_LOCSOFTWARE 0x00000004
16396 #define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008
16397 #define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010
16398 #define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020
16400 #define MA_DSCBSTART_LOOPING 0x00000001
16406 DWORD dwBufferBytes;
16408 WAVEFORMATEX* lpwfxFormat;
16409 GUID guid3DAlgorithm;
16416 DWORD dwBufferBytes;
16418 WAVEFORMATEX* lpwfxFormat;
16420 void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */
16421 } MA_DSCBUFFERDESC;
16427 DWORD dwMinSecondarySampleRate;
16428 DWORD dwMaxSecondarySampleRate;
16429 DWORD dwPrimaryBuffers;
16430 DWORD dwMaxHwMixingAllBuffers;
16431 DWORD dwMaxHwMixingStaticBuffers;
16432 DWORD dwMaxHwMixingStreamingBuffers;
16433 DWORD dwFreeHwMixingAllBuffers;
16434 DWORD dwFreeHwMixingStaticBuffers;
16435 DWORD dwFreeHwMixingStreamingBuffers;
16436 DWORD dwMaxHw3DAllBuffers;
16437 DWORD dwMaxHw3DStaticBuffers;
16438 DWORD dwMaxHw3DStreamingBuffers;
16439 DWORD dwFreeHw3DAllBuffers;
16440 DWORD dwFreeHw3DStaticBuffers;
16441 DWORD dwFreeHw3DStreamingBuffers;
16442 DWORD dwTotalHwMemBytes;
16443 DWORD dwFreeHwMemBytes;
16444 DWORD dwMaxContigFreeHwMemBytes;
16445 DWORD dwUnlockTransferRateHwBuffers;
16446 DWORD dwPlayCpuOverheadSwBuffers;
16455 DWORD dwBufferBytes;
16456 DWORD dwUnlockTransferRate;
16457 DWORD dwPlayCpuOverhead;
16472 DWORD dwBufferBytes;
16479 HANDLE hEventNotify;
16480 } MA_DSBPOSITIONNOTIFY;
16482 typedef struct ma_IDirectSound ma_IDirectSound;
16483 typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer;
16484 typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture;
16485 typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer;
16486 typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify;
16490 COM objects. The way these work is that you have a vtable (a list of function pointers, kind of
16491 like how C++ works internally), and then you have a structure with a single member, which is a
16492 pointer to the vtable. The vtable is where the methods of the object are defined. Methods need
16493 to be in a specific order, and parent classes need to have their methods declared first.
16500 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject);
16501 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis);
16502 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis);
16505 HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter);
16506 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps);
16507 HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate);
16508 HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel);
16509 HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis);
16510 HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig);
16511 HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig);
16512 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice);
16513 } ma_IDirectSoundVtbl;
16514 struct ma_IDirectSound
16516 ma_IDirectSoundVtbl* lpVtbl;
16518 static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
16519 static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); }
16520 static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); }
16521 static MA_INLINE HRESULT ma_IDirectSound_CreateSoundBuffer(ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateSoundBuffer(pThis, pDSBufferDesc, ppDSBuffer, pUnkOuter); }
16522 static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); }
16523 static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); }
16524 static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); }
16525 static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); }
16526 static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); }
16527 static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); }
16528 static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
16531 /* IDirectSoundBuffer */
16535 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject);
16536 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis);
16537 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis);
16539 /* IDirectSoundBuffer */
16540 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps);
16541 HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor);
16542 HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
16543 HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume);
16544 HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan);
16545 HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency);
16546 HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus);
16547 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc);
16548 HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
16549 HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags);
16550 HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition);
16551 HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat);
16552 HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume);
16553 HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan);
16554 HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency);
16555 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis);
16556 HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
16557 HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis);
16558 } ma_IDirectSoundBufferVtbl;
16559 struct ma_IDirectSoundBuffer
16561 ma_IDirectSoundBufferVtbl* lpVtbl;
16563 static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
16564 static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
16565 static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
16566 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); }
16567 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); }
16568 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
16569 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); }
16570 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); }
16571 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); }
16572 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
16573 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); }
16574 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Lock(ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); }
16575 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); }
16576 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); }
16577 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); }
16578 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); }
16579 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); }
16580 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); }
16581 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
16582 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Unlock(ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); }
16583 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); }
16586 /* IDirectSoundCapture */
16590 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject);
16591 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis);
16592 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis);
16594 /* IDirectSoundCapture */
16595 HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter);
16596 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps);
16597 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice);
16598 } ma_IDirectSoundCaptureVtbl;
16599 struct ma_IDirectSoundCapture
16601 ma_IDirectSoundCaptureVtbl* lpVtbl;
16603 static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
16604 static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef(ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); }
16605 static MA_INLINE ULONG ma_IDirectSoundCapture_Release(ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); }
16606 static MA_INLINE HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); }
16607 static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); }
16608 static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
16611 /* IDirectSoundCaptureBuffer */
16615 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject);
16616 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis);
16617 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis);
16619 /* IDirectSoundCaptureBuffer */
16620 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps);
16621 HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition);
16622 HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
16623 HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus);
16624 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc);
16625 HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
16626 HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags);
16627 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis);
16628 HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
16629 } ma_IDirectSoundCaptureBufferVtbl;
16630 struct ma_IDirectSoundCaptureBuffer
16632 ma_IDirectSoundCaptureBufferVtbl* lpVtbl;
16634 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
16635 static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
16636 static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
16637 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); }
16638 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); }
16639 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
16640 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
16641 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); }
16642 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Lock(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); }
16643 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); }
16644 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
16645 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Unlock(ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); }
16648 /* IDirectSoundNotify */
16652 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject);
16653 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis);
16654 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis);
16656 /* IDirectSoundNotify */
16657 HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies);
16658 } ma_IDirectSoundNotifyVtbl;
16659 struct ma_IDirectSoundNotify
16661 ma_IDirectSoundNotifyVtbl* lpVtbl;
16663 static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
16664 static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); }
16665 static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); }
16666 static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); }
16669 typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (LPGUID pDeviceGUID, LPCSTR pDeviceDescription, LPCSTR pModule, LPVOID pContext);
16670 typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, LPUNKNOWN pUnkOuter);
16671 typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext);
16672 typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, LPUNKNOWN pUnkOuter);
16673 typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext);
16675 static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax)
16677 /* Normalize the range in case we were given something stupid. */
16678 if (sampleRateMin < (ma_uint32)ma_standard_sample_rate_min) {
16679 sampleRateMin = (ma_uint32)ma_standard_sample_rate_min;
16681 if (sampleRateMax > (ma_uint32)ma_standard_sample_rate_max) {
16682 sampleRateMax = (ma_uint32)ma_standard_sample_rate_max;
16684 if (sampleRateMin > sampleRateMax) {
16685 sampleRateMin = sampleRateMax;
16688 if (sampleRateMin == sampleRateMax) {
16689 return sampleRateMax;
16691 size_t iStandardRate;
16692 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
16693 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
16694 if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) {
16695 return standardRate;
16700 /* Should never get here. */
16701 MA_ASSERT(MA_FALSE);
16706 Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown,
16707 the channel count and channel map will be left unmodified.
16709 static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut)
16715 if (pChannelsOut != NULL) {
16716 channels = *pChannelsOut;
16720 if (pChannelMapOut != NULL) {
16721 channelMap = *pChannelMapOut;
16725 The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper
16726 16 bits is for the geometry.
16728 switch ((BYTE)(speakerConfig)) {
16729 case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
16730 case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break;
16731 case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
16732 case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
16733 case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break;
16734 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;
16735 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;
16736 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;
16737 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;
16741 if (pChannelsOut != NULL) {
16742 *pChannelsOut = channels;
16745 if (pChannelMapOut != NULL) {
16746 *pChannelMapOut = channelMap;
16751 static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound)
16753 ma_IDirectSound* pDirectSound;
16757 MA_ASSERT(pContext != NULL);
16758 MA_ASSERT(ppDirectSound != NULL);
16760 *ppDirectSound = NULL;
16761 pDirectSound = NULL;
16763 if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) {
16764 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
16767 /* The cooperative level must be set before doing anything else. */
16768 hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();
16769 if (hWnd == NULL) {
16770 hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();
16773 hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY);
16775 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.", ma_result_from_HRESULT(hr));
16778 *ppDirectSound = pDirectSound;
16782 static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture)
16784 ma_IDirectSoundCapture* pDirectSoundCapture;
16787 MA_ASSERT(pContext != NULL);
16788 MA_ASSERT(ppDirectSoundCapture != NULL);
16790 /* DirectSound does not support exclusive mode for capture. */
16791 if (shareMode == ma_share_mode_exclusive) {
16792 return MA_SHARE_MODE_NOT_SUPPORTED;
16795 *ppDirectSoundCapture = NULL;
16796 pDirectSoundCapture = NULL;
16798 hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL);
16800 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device.", ma_result_from_HRESULT(hr));
16803 *ppDirectSoundCapture = pDirectSoundCapture;
16807 static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate)
16811 WORD bitsPerSample;
16814 MA_ASSERT(pContext != NULL);
16815 MA_ASSERT(pDirectSoundCapture != NULL);
16820 if (pBitsPerSample) {
16821 *pBitsPerSample = 0;
16827 MA_ZERO_OBJECT(&caps);
16828 caps.dwSize = sizeof(caps);
16829 hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps);
16831 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device.", ma_result_from_HRESULT(hr));
16835 *pChannels = (WORD)caps.dwChannels;
16838 /* 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. */
16839 bitsPerSample = 16;
16840 sampleRate = 48000;
16842 if (caps.dwChannels == 1) {
16843 if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) {
16844 sampleRate = 48000;
16845 } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) {
16846 sampleRate = 44100;
16847 } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) {
16848 sampleRate = 22050;
16849 } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) {
16850 sampleRate = 11025;
16851 } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) {
16852 sampleRate = 96000;
16855 if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) {
16856 sampleRate = 48000;
16857 } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) {
16858 sampleRate = 44100;
16859 } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) {
16860 sampleRate = 22050;
16861 } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) {
16862 sampleRate = 11025;
16863 } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) {
16864 sampleRate = 96000;
16866 bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
16869 } else if (caps.dwChannels == 2) {
16870 if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) {
16871 sampleRate = 48000;
16872 } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) {
16873 sampleRate = 44100;
16874 } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) {
16875 sampleRate = 22050;
16876 } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) {
16877 sampleRate = 11025;
16878 } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) {
16879 sampleRate = 96000;
16882 if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) {
16883 sampleRate = 48000;
16884 } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) {
16885 sampleRate = 44100;
16886 } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) {
16887 sampleRate = 22050;
16888 } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) {
16889 sampleRate = 11025;
16890 } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) {
16891 sampleRate = 96000;
16893 bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
16898 if (pBitsPerSample) {
16899 *pBitsPerSample = bitsPerSample;
16902 *pSampleRate = sampleRate;
16911 ma_context* pContext;
16912 ma_device_type deviceType;
16913 ma_enum_devices_callback_proc callback;
16915 ma_bool32 terminated;
16916 } ma_context_enumerate_devices_callback_data__dsound;
16918 static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
16920 ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext;
16921 ma_device_info deviceInfo;
16923 (void)lpcstrModule;
16925 MA_ZERO_OBJECT(&deviceInfo);
16928 if (lpGuid != NULL) {
16929 MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16);
16931 MA_ZERO_MEMORY(deviceInfo.id.dsound, 16);
16932 deviceInfo.isDefault = MA_TRUE;
16935 /* Name / Description */
16936 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1);
16939 /* Call the callback function, but make sure we stop enumerating if the callee requested so. */
16940 MA_ASSERT(pData != NULL);
16941 pData->terminated = !pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData);
16942 if (pData->terminated) {
16943 return FALSE; /* Stop enumeration. */
16945 return TRUE; /* Continue enumeration. */
16949 static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
16951 ma_context_enumerate_devices_callback_data__dsound data;
16953 MA_ASSERT(pContext != NULL);
16954 MA_ASSERT(callback != NULL);
16956 data.pContext = pContext;
16957 data.callback = callback;
16958 data.pUserData = pUserData;
16959 data.terminated = MA_FALSE;
16962 if (!data.terminated) {
16963 data.deviceType = ma_device_type_playback;
16964 ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
16968 if (!data.terminated) {
16969 data.deviceType = ma_device_type_capture;
16970 ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
16979 const ma_device_id* pDeviceID;
16980 ma_device_info* pDeviceInfo;
16982 } ma_context_get_device_info_callback_data__dsound;
16984 static BOOL CALLBACK ma_context_get_device_info_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
16986 ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext;
16987 MA_ASSERT(pData != NULL);
16989 if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) {
16990 /* Default device. */
16991 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
16992 pData->pDeviceInfo->isDefault = MA_TRUE;
16993 pData->found = MA_TRUE;
16994 return FALSE; /* Stop enumeration. */
16996 /* Not the default device. */
16997 if (lpGuid != NULL && pData->pDeviceID != NULL) {
16998 if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) {
16999 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
17000 pData->found = MA_TRUE;
17001 return FALSE; /* Stop enumeration. */
17006 (void)lpcstrModule;
17010 static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
17015 if (pDeviceID != NULL) {
17016 ma_context_get_device_info_callback_data__dsound data;
17019 MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16);
17021 /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */
17022 data.pDeviceID = pDeviceID;
17023 data.pDeviceInfo = pDeviceInfo;
17024 data.found = MA_FALSE;
17025 if (deviceType == ma_device_type_playback) {
17026 ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
17028 ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
17032 return MA_NO_DEVICE;
17035 /* 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. */
17038 MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16);
17040 /* Name / Description */
17041 if (deviceType == ma_device_type_playback) {
17042 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
17044 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
17047 pDeviceInfo->isDefault = MA_TRUE;
17050 /* Retrieving detailed information is slightly different depending on the device type. */
17051 if (deviceType == ma_device_type_playback) {
17053 ma_IDirectSound* pDirectSound;
17057 result = ma_context_create_IDirectSound__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSound);
17058 if (result != MA_SUCCESS) {
17062 MA_ZERO_OBJECT(&caps);
17063 caps.dwSize = sizeof(caps);
17064 hr = ma_IDirectSound_GetCaps(pDirectSound, &caps);
17066 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.", ma_result_from_HRESULT(hr));
17070 /* Channels. Only a single channel count is reported for DirectSound. */
17071 if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
17072 /* It supports at least stereo, but could support more. */
17073 DWORD speakerConfig;
17077 /* Look at the speaker configuration to get a better idea on the channel count. */
17078 hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig);
17079 if (SUCCEEDED(hr)) {
17080 ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL);
17083 /* It does not support stereo, which means we are stuck with mono. */
17089 In DirectSound, our native formats are centered around sample rates. All formats are supported, and we're only reporting a single channel
17090 count. However, DirectSound can report a range of supported sample rates. We're only going to include standard rates known by miniaudio
17091 in order to keep the size of this within reason.
17093 if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
17094 /* Multiple sample rates are supported. We'll report in order of our preferred sample rates. */
17095 size_t iStandardSampleRate;
17096 for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {
17097 ma_uint32 sampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];
17098 if (sampleRate >= caps.dwMinSecondarySampleRate && sampleRate <= caps.dwMaxSecondarySampleRate) {
17099 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown;
17100 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
17101 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
17102 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
17103 pDeviceInfo->nativeDataFormatCount += 1;
17107 /* Only a single sample rate is supported. */
17108 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown;
17109 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
17110 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = caps.dwMaxSecondarySampleRate;
17111 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
17112 pDeviceInfo->nativeDataFormatCount += 1;
17115 ma_IDirectSound_Release(pDirectSound);
17118 Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture
17119 devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just
17120 reporting the best format.
17122 ma_IDirectSoundCapture* pDirectSoundCapture;
17124 WORD bitsPerSample;
17127 result = ma_context_create_IDirectSoundCapture__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSoundCapture);
17128 if (result != MA_SUCCESS) {
17132 result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate);
17133 if (result != MA_SUCCESS) {
17134 ma_IDirectSoundCapture_Release(pDirectSoundCapture);
17138 ma_IDirectSoundCapture_Release(pDirectSoundCapture);
17140 /* The format is always an integer format and is based on the bits per sample. */
17141 if (bitsPerSample == 8) {
17142 pDeviceInfo->formats[0] = ma_format_u8;
17143 } else if (bitsPerSample == 16) {
17144 pDeviceInfo->formats[0] = ma_format_s16;
17145 } else if (bitsPerSample == 24) {
17146 pDeviceInfo->formats[0] = ma_format_s24;
17147 } else if (bitsPerSample == 32) {
17148 pDeviceInfo->formats[0] = ma_format_s32;
17150 return MA_FORMAT_NOT_SUPPORTED;
17153 pDeviceInfo->nativeDataFormats[0].channels = channels;
17154 pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate;
17155 pDeviceInfo->nativeDataFormats[0].flags = 0;
17156 pDeviceInfo->nativeDataFormatCount = 1;
17164 static ma_result ma_device_uninit__dsound(ma_device* pDevice)
17166 MA_ASSERT(pDevice != NULL);
17168 if (pDevice->dsound.pCaptureBuffer != NULL) {
17169 ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
17171 if (pDevice->dsound.pCapture != NULL) {
17172 ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture);
17175 if (pDevice->dsound.pPlaybackBuffer != NULL) {
17176 ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
17178 if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) {
17179 ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer);
17181 if (pDevice->dsound.pPlayback != NULL) {
17182 ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback);
17188 static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, WAVEFORMATEXTENSIBLE* pWF)
17192 if (format == ma_format_unknown) {
17193 format = MA_DEFAULT_FORMAT;
17196 if (channels == 0) {
17197 channels = MA_DEFAULT_CHANNELS;
17200 if (sampleRate == 0) {
17201 sampleRate = MA_DEFAULT_SAMPLE_RATE;
17207 case ma_format_s16:
17208 case ma_format_s24:
17209 /*case ma_format_s24_32:*/
17210 case ma_format_s32:
17212 subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
17215 case ma_format_f32:
17217 subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
17221 return MA_FORMAT_NOT_SUPPORTED;
17224 MA_ZERO_OBJECT(pWF);
17225 pWF->Format.cbSize = sizeof(*pWF);
17226 pWF->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
17227 pWF->Format.nChannels = (WORD)channels;
17228 pWF->Format.nSamplesPerSec = (DWORD)sampleRate;
17229 pWF->Format.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8);
17230 pWF->Format.nBlockAlign = (WORD)(pWF->Format.nChannels * pWF->Format.wBitsPerSample / 8);
17231 pWF->Format.nAvgBytesPerSec = pWF->Format.nBlockAlign * pWF->Format.nSamplesPerSec;
17232 pWF->Samples.wValidBitsPerSample = pWF->Format.wBitsPerSample;
17233 pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels);
17234 pWF->SubFormat = subformat;
17239 static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
17241 /* DirectSound has a minimum period size of 20ms. */
17242 ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(20, nativeSampleRate);
17243 ma_uint32 periodSizeInFrames;
17245 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile);
17246 if (periodSizeInFrames < minPeriodSizeInFrames) {
17247 periodSizeInFrames = minPeriodSizeInFrames;
17250 return periodSizeInFrames;
17253 static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
17258 MA_ASSERT(pDevice != NULL);
17260 MA_ZERO_OBJECT(&pDevice->dsound);
17262 if (pConfig->deviceType == ma_device_type_loopback) {
17263 return MA_DEVICE_TYPE_NOT_SUPPORTED;
17267 Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize
17268 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
17271 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
17272 WAVEFORMATEXTENSIBLE wf;
17273 MA_DSCBUFFERDESC descDS;
17274 ma_uint32 periodSizeInFrames;
17275 ma_uint32 periodCount;
17276 char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
17277 WAVEFORMATEXTENSIBLE* pActualFormat;
17279 result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf);
17280 if (result != MA_SUCCESS) {
17284 result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture);
17285 if (result != MA_SUCCESS) {
17286 ma_device_uninit__dsound(pDevice);
17290 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);
17291 if (result != MA_SUCCESS) {
17292 ma_device_uninit__dsound(pDevice);
17296 wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8);
17297 wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
17298 wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample;
17299 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
17301 /* The size of the buffer must be a clean multiple of the period count. */
17302 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.Format.nSamplesPerSec, pConfig->performanceProfile);
17303 periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS;
17305 MA_ZERO_OBJECT(&descDS);
17306 descDS.dwSize = sizeof(descDS);
17307 descDS.dwFlags = 0;
17308 descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.Format.nBlockAlign;
17309 descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
17310 hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL);
17312 ma_device_uninit__dsound(pDevice);
17313 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", ma_result_from_HRESULT(hr));
17316 /* Get the _actual_ properties of the buffer. */
17317 pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata;
17318 hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);
17320 ma_device_uninit__dsound(pDevice);
17321 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));
17324 /* We can now start setting the output data formats. */
17325 pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat);
17326 pDescriptorCapture->channels = pActualFormat->Format.nChannels;
17327 pDescriptorCapture->sampleRate = pActualFormat->Format.nSamplesPerSec;
17329 /* Get the native channel map based on the channel mask. */
17330 if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
17331 ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
17333 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
17337 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
17338 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.
17340 if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) {
17341 descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount;
17342 ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
17344 hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL);
17346 ma_device_uninit__dsound(pDevice);
17347 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", ma_result_from_HRESULT(hr));
17351 /* DirectSound should give us a buffer exactly the size we asked for. */
17352 pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
17353 pDescriptorCapture->periodCount = periodCount;
17356 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
17357 WAVEFORMATEXTENSIBLE wf;
17358 MA_DSBUFFERDESC descDSPrimary;
17360 char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
17361 WAVEFORMATEXTENSIBLE* pActualFormat;
17362 ma_uint32 periodSizeInFrames;
17363 ma_uint32 periodCount;
17364 MA_DSBUFFERDESC descDS;
17366 result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf);
17367 if (result != MA_SUCCESS) {
17371 result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback);
17372 if (result != MA_SUCCESS) {
17373 ma_device_uninit__dsound(pDevice);
17377 MA_ZERO_OBJECT(&descDSPrimary);
17378 descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC);
17379 descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME;
17380 hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL);
17382 ma_device_uninit__dsound(pDevice);
17383 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer.", ma_result_from_HRESULT(hr));
17387 /* We may want to make some adjustments to the format if we are using defaults. */
17388 MA_ZERO_OBJECT(&caps);
17389 caps.dwSize = sizeof(caps);
17390 hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps);
17392 ma_device_uninit__dsound(pDevice);
17393 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.", ma_result_from_HRESULT(hr));
17396 if (pDescriptorPlayback->channels == 0) {
17397 if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
17398 DWORD speakerConfig;
17400 /* It supports at least stereo, but could support more. */
17401 wf.Format.nChannels = 2;
17403 /* Look at the speaker configuration to get a better idea on the channel count. */
17404 if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) {
17405 ma_get_channels_from_speaker_config__dsound(speakerConfig, &wf.Format.nChannels, &wf.dwChannelMask);
17408 /* It does not support stereo, which means we are stuck with mono. */
17409 wf.Format.nChannels = 1;
17413 if (pDescriptorPlayback->sampleRate == 0) {
17414 /* We base the sample rate on the values returned by GetCaps(). */
17415 if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
17416 wf.Format.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate);
17418 wf.Format.nSamplesPerSec = caps.dwMaxSecondarySampleRate;
17422 wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8);
17423 wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
17428 The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest
17429 supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer
17430 and compare the result with the format that was requested with the SetFormat method.
17432 hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)&wf);
17434 ma_device_uninit__dsound(pDevice);
17435 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));
17438 /* Get the _actual_ properties of the buffer. */
17439 pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata;
17440 hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);
17442 ma_device_uninit__dsound(pDevice);
17443 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));
17446 /* We now have enough information to start setting some output properties. */
17447 pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat);
17448 pDescriptorPlayback->channels = pActualFormat->Format.nChannels;
17449 pDescriptorPlayback->sampleRate = pActualFormat->Format.nSamplesPerSec;
17451 /* Get the internal channel map based on the channel mask. */
17452 if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
17453 ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
17455 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
17458 /* The size of the buffer must be a clean multiple of the period count. */
17459 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
17460 periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS;
17463 Meaning of dwFlags (from MSDN):
17465 DSBCAPS_CTRLPOSITIONNOTIFY
17466 The buffer has position notification capability.
17468 DSBCAPS_GLOBALFOCUS
17469 With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to
17470 another application, even if the new application uses DirectSound.
17472 DSBCAPS_GETCURRENTPOSITION2
17473 In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated
17474 sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the
17475 application can get a more accurate play cursor.
17477 MA_ZERO_OBJECT(&descDS);
17478 descDS.dwSize = sizeof(descDS);
17479 descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2;
17480 descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels);
17481 descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
17482 hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL);
17484 ma_device_uninit__dsound(pDevice);
17485 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.", ma_result_from_HRESULT(hr));
17488 /* DirectSound should give us a buffer exactly the size we asked for. */
17489 pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
17490 pDescriptorPlayback->periodCount = periodCount;
17497 static ma_result ma_device_data_loop__dsound(ma_device* pDevice)
17499 ma_result result = MA_SUCCESS;
17500 ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
17501 ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
17503 DWORD lockOffsetInBytesCapture;
17504 DWORD lockSizeInBytesCapture;
17505 DWORD mappedSizeInBytesCapture;
17506 DWORD mappedDeviceFramesProcessedCapture;
17507 void* pMappedDeviceBufferCapture;
17508 DWORD lockOffsetInBytesPlayback;
17509 DWORD lockSizeInBytesPlayback;
17510 DWORD mappedSizeInBytesPlayback;
17511 void* pMappedDeviceBufferPlayback;
17512 DWORD prevReadCursorInBytesCapture = 0;
17513 DWORD prevPlayCursorInBytesPlayback = 0;
17514 ma_bool32 physicalPlayCursorLoopFlagPlayback = 0;
17515 DWORD virtualWriteCursorInBytesPlayback = 0;
17516 ma_bool32 virtualWriteCursorLoopFlagPlayback = 0;
17517 ma_bool32 isPlaybackDeviceStarted = MA_FALSE;
17518 ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */
17519 ma_uint32 waitTimeInMilliseconds = 1;
17521 MA_ASSERT(pDevice != NULL);
17523 /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */
17524 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
17525 if (FAILED(ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING))) {
17526 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
17530 while (ma_device_get_state(pDevice) == MA_STATE_STARTED) {
17531 switch (pDevice->type)
17533 case ma_device_type_duplex:
17535 DWORD physicalCaptureCursorInBytes;
17536 DWORD physicalReadCursorInBytes;
17537 hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);
17539 return ma_result_from_HRESULT(hr);
17542 /* If nothing is available we just sleep for a bit and return from this iteration. */
17543 if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) {
17544 ma_sleep(waitTimeInMilliseconds);
17545 continue; /* Nothing is available in the capture buffer. */
17549 The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure
17550 we don't return until every frame has been copied over.
17552 if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
17553 /* The capture position has not looped. This is the simple case. */
17554 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
17555 lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
17558 The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
17559 do it again from the start.
17561 if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
17562 /* Lock up to the end of the buffer. */
17563 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
17564 lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
17566 /* Lock starting from the start of the buffer. */
17567 lockOffsetInBytesCapture = 0;
17568 lockSizeInBytesCapture = physicalReadCursorInBytes;
17572 if (lockSizeInBytesCapture == 0) {
17573 ma_sleep(waitTimeInMilliseconds);
17574 continue; /* Nothing is available in the capture buffer. */
17577 hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
17579 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));
17583 /* 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. */
17584 mappedDeviceFramesProcessedCapture = 0;
17586 for (;;) { /* Keep writing to the playback device. */
17587 ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
17588 ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
17589 ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
17590 ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
17591 ma_uint32 outputFramesInClientFormatCount;
17592 ma_uint32 outputFramesInClientFormatConsumed = 0;
17593 ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap);
17594 ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture;
17595 void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture);
17597 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess);
17598 if (result != MA_SUCCESS) {
17602 outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess;
17603 mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess;
17605 ma_device__on_data(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess);
17607 /* 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. */
17609 ma_uint32 framesWrittenThisIteration;
17610 DWORD physicalPlayCursorInBytes;
17611 DWORD physicalWriteCursorInBytes;
17612 DWORD availableBytesPlayback;
17613 DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */
17615 /* We need the physical play and write cursors. */
17616 if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
17620 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
17621 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
17623 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
17625 /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
17626 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
17627 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
17628 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
17629 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
17630 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
17632 /* This is an error. */
17633 #ifdef MA_DEBUG_OUTPUT
17634 printf("[DirectSound] (Duplex/Playback) WARNING: Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
17636 availableBytesPlayback = 0;
17639 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
17640 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
17641 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
17643 /* This is an error. */
17644 #ifdef MA_DEBUG_OUTPUT
17645 printf("[DirectSound] (Duplex/Playback) WARNING: Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
17647 availableBytesPlayback = 0;
17651 #ifdef MA_DEBUG_OUTPUT
17652 /*printf("[DirectSound] (Duplex/Playback) physicalPlayCursorInBytes=%d, availableBytesPlayback=%d\n", physicalPlayCursorInBytes, availableBytesPlayback);*/
17655 /* If there's no room available for writing we need to wait for more. */
17656 if (availableBytesPlayback == 0) {
17657 /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
17658 if (!isPlaybackDeviceStarted) {
17659 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
17661 ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
17662 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr));
17664 isPlaybackDeviceStarted = MA_TRUE;
17666 ma_sleep(waitTimeInMilliseconds);
17672 /* 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. */
17673 lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
17674 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
17675 /* Same loop iteration. Go up to the end of the buffer. */
17676 lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
17678 /* Different loop iterations. Go up to the physical play cursor. */
17679 lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
17682 hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
17684 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));
17689 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
17690 endless glitching due to it constantly running out of data.
17692 if (isPlaybackDeviceStarted) {
17693 DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback;
17694 if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) {
17695 silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback;
17696 if (silentPaddingInBytes > lockSizeInBytesPlayback) {
17697 silentPaddingInBytes = lockSizeInBytesPlayback;
17700 #ifdef MA_DEBUG_OUTPUT
17701 printf("[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%ld, silentPaddingInBytes=%ld\n", availableBytesPlayback, silentPaddingInBytes);
17706 /* At this point we have a buffer for output. */
17707 if (silentPaddingInBytes > 0) {
17708 MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes);
17709 framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback;
17711 ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed);
17712 ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback;
17713 void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback);
17714 void* pConvertedFramesOut = pMappedDeviceBufferPlayback;
17716 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut);
17717 if (result != MA_SUCCESS) {
17721 outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut;
17722 framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut;
17726 hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0);
17728 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));
17732 virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback;
17733 if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) {
17734 virtualWriteCursorInBytesPlayback = 0;
17735 virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
17739 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
17740 a bit of a buffer to prevent the playback buffer from getting starved.
17742 framesWrittenToPlaybackDevice += framesWrittenThisIteration;
17743 if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) {
17744 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
17746 ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
17747 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr));
17749 isPlaybackDeviceStarted = MA_TRUE;
17752 if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) {
17753 break; /* We're finished with the output data.*/
17757 if (clientCapturedFramesToProcess == 0) {
17758 break; /* We just consumed every input sample. */
17763 /* At this point we're done with the mapped portion of the capture buffer. */
17764 hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
17766 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));
17768 prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture);
17773 case ma_device_type_capture:
17775 DWORD physicalCaptureCursorInBytes;
17776 DWORD physicalReadCursorInBytes;
17777 hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);
17782 /* If the previous capture position is the same as the current position we need to wait a bit longer. */
17783 if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) {
17784 ma_sleep(waitTimeInMilliseconds);
17788 /* Getting here means we have capture data available. */
17789 if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
17790 /* The capture position has not looped. This is the simple case. */
17791 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
17792 lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
17795 The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
17796 do it again from the start.
17798 if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
17799 /* Lock up to the end of the buffer. */
17800 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
17801 lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
17803 /* Lock starting from the start of the buffer. */
17804 lockOffsetInBytesCapture = 0;
17805 lockSizeInBytesCapture = physicalReadCursorInBytes;
17809 #ifdef MA_DEBUG_OUTPUT
17810 /*printf("[DirectSound] (Capture) physicalCaptureCursorInBytes=%d, physicalReadCursorInBytes=%d\n", physicalCaptureCursorInBytes, physicalReadCursorInBytes);*/
17811 /*printf("[DirectSound] (Capture) lockOffsetInBytesCapture=%d, lockSizeInBytesCapture=%d\n", lockOffsetInBytesCapture, lockSizeInBytesCapture);*/
17814 if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) {
17815 ma_sleep(waitTimeInMilliseconds);
17816 continue; /* Nothing is available in the capture buffer. */
17819 hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
17821 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));
17824 #ifdef MA_DEBUG_OUTPUT
17825 if (lockSizeInBytesCapture != mappedSizeInBytesCapture) {
17826 printf("[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture);
17830 ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture);
17832 hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
17834 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));
17836 prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture;
17838 if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) {
17839 prevReadCursorInBytesCapture = 0;
17845 case ma_device_type_playback:
17847 DWORD availableBytesPlayback;
17848 DWORD physicalPlayCursorInBytes;
17849 DWORD physicalWriteCursorInBytes;
17850 hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);
17855 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
17856 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
17858 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
17860 /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
17861 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
17862 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
17863 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
17864 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
17865 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
17867 /* This is an error. */
17868 #ifdef MA_DEBUG_OUTPUT
17869 printf("[DirectSound] (Playback) WARNING: Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
17871 availableBytesPlayback = 0;
17874 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
17875 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
17876 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
17878 /* This is an error. */
17879 #ifdef MA_DEBUG_OUTPUT
17880 printf("[DirectSound] (Playback) WARNING: Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
17882 availableBytesPlayback = 0;
17886 #ifdef MA_DEBUG_OUTPUT
17887 /*printf("[DirectSound] (Playback) physicalPlayCursorInBytes=%d, availableBytesPlayback=%d\n", physicalPlayCursorInBytes, availableBytesPlayback);*/
17890 /* If there's no room available for writing we need to wait for more. */
17891 if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) {
17892 /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
17893 if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) {
17894 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
17896 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr));
17898 isPlaybackDeviceStarted = MA_TRUE;
17900 ma_sleep(waitTimeInMilliseconds);
17905 /* 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. */
17906 lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
17907 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
17908 /* Same loop iteration. Go up to the end of the buffer. */
17909 lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
17911 /* Different loop iterations. Go up to the physical play cursor. */
17912 lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
17915 hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
17917 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));
17921 /* At this point we have a buffer for output. */
17922 ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback);
17924 hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0);
17926 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));
17930 virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback;
17931 if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) {
17932 virtualWriteCursorInBytesPlayback = 0;
17933 virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
17937 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
17938 a bit of a buffer to prevent the playback buffer from getting starved.
17940 framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback;
17941 if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) {
17942 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
17944 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr));
17946 isPlaybackDeviceStarted = MA_TRUE;
17951 default: return MA_INVALID_ARGS; /* Invalid device type. */
17954 if (result != MA_SUCCESS) {
17959 /* Getting here means the device is being stopped. */
17960 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
17961 hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
17963 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed.", ma_result_from_HRESULT(hr));
17967 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
17968 /* 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. */
17969 if (isPlaybackDeviceStarted) {
17971 DWORD availableBytesPlayback = 0;
17972 DWORD physicalPlayCursorInBytes;
17973 DWORD physicalWriteCursorInBytes;
17974 hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);
17979 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
17980 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
17982 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
17984 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
17985 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
17986 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
17987 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
17988 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
17993 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
17994 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
17995 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
18001 if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) {
18005 ma_sleep(waitTimeInMilliseconds);
18009 hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
18011 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed.", ma_result_from_HRESULT(hr));
18014 ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0);
18020 static ma_result ma_context_uninit__dsound(ma_context* pContext)
18022 MA_ASSERT(pContext != NULL);
18023 MA_ASSERT(pContext->backend == ma_backend_dsound);
18025 ma_dlclose(pContext, pContext->dsound.hDSoundDLL);
18030 static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
18032 MA_ASSERT(pContext != NULL);
18036 pContext->dsound.hDSoundDLL = ma_dlopen(pContext, "dsound.dll");
18037 if (pContext->dsound.hDSoundDLL == NULL) {
18038 return MA_API_NOT_FOUND;
18041 pContext->dsound.DirectSoundCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCreate");
18042 pContext->dsound.DirectSoundEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA");
18043 pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate");
18044 pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA");
18046 pCallbacks->onContextInit = ma_context_init__dsound;
18047 pCallbacks->onContextUninit = ma_context_uninit__dsound;
18048 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound;
18049 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__dsound;
18050 pCallbacks->onDeviceInit = ma_device_init__dsound;
18051 pCallbacks->onDeviceUninit = ma_device_uninit__dsound;
18052 pCallbacks->onDeviceStart = NULL; /* Not used. Started in onDeviceDataLoop. */
18053 pCallbacks->onDeviceStop = NULL; /* Not used. Stopped in onDeviceDataLoop. */
18054 pCallbacks->onDeviceRead = NULL; /* Not used. Data is read directly in onDeviceDataLoop. */
18055 pCallbacks->onDeviceWrite = NULL; /* Not used. Data is written directly in onDeviceDataLoop. */
18056 pCallbacks->onDeviceDataLoop = ma_device_data_loop__dsound;
18064 /******************************************************************************
18068 ******************************************************************************/
18069 #ifdef MA_HAS_WINMM
18072 Some older compilers don't have WAVEOUTCAPS2A and WAVEINCAPS2A, so we'll need to write this ourselves. These structures
18073 are exactly the same as the older ones but they have a few GUIDs for manufacturer/product/name identification. I'm keeping
18074 the names the same as the Win32 library for consistency, but namespaced to avoid naming conflicts with the Win32 version.
18080 MMVERSION vDriverVersion;
18081 CHAR szPname[MAXPNAMELEN];
18086 GUID ManufacturerGuid;
18089 } MA_WAVEOUTCAPS2A;
18094 MMVERSION vDriverVersion;
18095 CHAR szPname[MAXPNAMELEN];
18099 GUID ManufacturerGuid;
18104 typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void);
18105 typedef MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc);
18106 typedef MMRESULT (WINAPI * MA_PFN_waveOutOpen)(LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
18107 typedef MMRESULT (WINAPI * MA_PFN_waveOutClose)(HWAVEOUT hwo);
18108 typedef MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
18109 typedef MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
18110 typedef MMRESULT (WINAPI * MA_PFN_waveOutWrite)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
18111 typedef MMRESULT (WINAPI * MA_PFN_waveOutReset)(HWAVEOUT hwo);
18112 typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void);
18113 typedef MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic);
18114 typedef MMRESULT (WINAPI * MA_PFN_waveInOpen)(LPHWAVEIN phwi, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
18115 typedef MMRESULT (WINAPI * MA_PFN_waveInClose)(HWAVEIN hwi);
18116 typedef MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
18117 typedef MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
18118 typedef MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
18119 typedef MMRESULT (WINAPI * MA_PFN_waveInStart)(HWAVEIN hwi);
18120 typedef MMRESULT (WINAPI * MA_PFN_waveInReset)(HWAVEIN hwi);
18122 static ma_result ma_result_from_MMRESULT(MMRESULT resultMM)
18124 switch (resultMM) {
18125 case MMSYSERR_NOERROR: return MA_SUCCESS;
18126 case MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS;
18127 case MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS;
18128 case MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY;
18129 case MMSYSERR_INVALFLAG: return MA_INVALID_ARGS;
18130 case MMSYSERR_INVALPARAM: return MA_INVALID_ARGS;
18131 case MMSYSERR_HANDLEBUSY: return MA_BUSY;
18132 case MMSYSERR_ERROR: return MA_ERROR;
18133 default: return MA_ERROR;
18137 static char* ma_find_last_character(char* str, char ch)
18146 while (*str != '\0') {
18157 static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels)
18159 return periodSizeInFrames * ma_get_bytes_per_frame(format, channels);
18164 Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so
18165 we can do things generically and typesafely. Names are being kept the same for consistency.
18169 CHAR szPname[MAXPNAMELEN];
18175 static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate)
18177 WORD bitsPerSample = 0;
18178 DWORD sampleRate = 0;
18180 if (pBitsPerSample) {
18181 *pBitsPerSample = 0;
18187 if (channels == 1) {
18188 bitsPerSample = 16;
18189 if ((dwFormats & WAVE_FORMAT_48M16) != 0) {
18190 sampleRate = 48000;
18191 } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {
18192 sampleRate = 44100;
18193 } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {
18194 sampleRate = 22050;
18195 } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {
18196 sampleRate = 11025;
18197 } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {
18198 sampleRate = 96000;
18201 if ((dwFormats & WAVE_FORMAT_48M08) != 0) {
18202 sampleRate = 48000;
18203 } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {
18204 sampleRate = 44100;
18205 } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {
18206 sampleRate = 22050;
18207 } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {
18208 sampleRate = 11025;
18209 } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {
18210 sampleRate = 96000;
18212 return MA_FORMAT_NOT_SUPPORTED;
18216 bitsPerSample = 16;
18217 if ((dwFormats & WAVE_FORMAT_48S16) != 0) {
18218 sampleRate = 48000;
18219 } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {
18220 sampleRate = 44100;
18221 } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {
18222 sampleRate = 22050;
18223 } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {
18224 sampleRate = 11025;
18225 } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {
18226 sampleRate = 96000;
18229 if ((dwFormats & WAVE_FORMAT_48S08) != 0) {
18230 sampleRate = 48000;
18231 } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {
18232 sampleRate = 44100;
18233 } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {
18234 sampleRate = 22050;
18235 } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {
18236 sampleRate = 11025;
18237 } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {
18238 sampleRate = 96000;
18240 return MA_FORMAT_NOT_SUPPORTED;
18245 if (pBitsPerSample) {
18246 *pBitsPerSample = bitsPerSample;
18249 *pSampleRate = sampleRate;
18255 static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, WAVEFORMATEX* pWF)
18259 MA_ASSERT(pWF != NULL);
18261 MA_ZERO_OBJECT(pWF);
18262 pWF->cbSize = sizeof(*pWF);
18263 pWF->wFormatTag = WAVE_FORMAT_PCM;
18264 pWF->nChannels = (WORD)channels;
18265 if (pWF->nChannels > 2) {
18266 pWF->nChannels = 2;
18269 result = ma_get_best_info_from_formats_flags__winmm(dwFormats, channels, &pWF->wBitsPerSample, &pWF->nSamplesPerSec);
18270 if (result != MA_SUCCESS) {
18274 pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8);
18275 pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec;
18280 static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo)
18282 WORD bitsPerSample;
18286 MA_ASSERT(pContext != NULL);
18287 MA_ASSERT(pCaps != NULL);
18288 MA_ASSERT(pDeviceInfo != NULL);
18293 Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking
18294 situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try
18295 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.
18298 /* Set the default to begin with. */
18299 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1);
18302 Now try the registry. There's a few things to consider here:
18303 - The name GUID can be null, in which we case we just need to stick to the original 31 characters.
18304 - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters.
18305 - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The
18306 problem, however is that WASAPI and DirectSound use "<component> (<name>)" format (such as "Speakers (High Definition Audio)"),
18307 but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to
18308 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
18309 name, and then concatenate the name from the registry.
18311 if (!ma_is_guid_null(&pCaps->NameGuid)) {
18312 wchar_t guidStrW[256];
18313 if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) {
18318 WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE);
18320 ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\");
18321 ma_strcat_s(keyStr, sizeof(keyStr), guidStr);
18323 if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
18324 BYTE nameFromReg[512];
18325 DWORD nameFromRegSize = sizeof(nameFromReg);
18326 result = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize);
18327 ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey);
18329 if (result == ERROR_SUCCESS) {
18330 /* We have the value from the registry, so now we need to construct the name string. */
18332 if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) {
18333 char* nameBeg = ma_find_last_character(name, '(');
18334 if (nameBeg != NULL) {
18335 size_t leadingLen = (nameBeg - name);
18336 ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1);
18338 /* The closing ")", if it can fit. */
18339 if (leadingLen + nameFromRegSize < sizeof(name)-1) {
18340 ma_strcat_s(name, sizeof(name), ")");
18343 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1);
18352 result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate);
18353 if (result != MA_SUCCESS) {
18357 if (bitsPerSample == 8) {
18358 pDeviceInfo->nativeDataFormats[0].format = ma_format_u8;
18359 } else if (bitsPerSample == 16) {
18360 pDeviceInfo->nativeDataFormats[0].format = ma_format_s16;
18361 } else if (bitsPerSample == 24) {
18362 pDeviceInfo->nativeDataFormats[0].format = ma_format_s24;
18363 } else if (bitsPerSample == 32) {
18364 pDeviceInfo->nativeDataFormats[0].format = ma_format_s32;
18366 return MA_FORMAT_NOT_SUPPORTED;
18368 pDeviceInfo->nativeDataFormats[0].channels = pCaps->wChannels;
18369 pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate;
18370 pDeviceInfo->nativeDataFormats[0].flags = 0;
18371 pDeviceInfo->nativeDataFormatCount = 1;
18376 static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo)
18380 MA_ASSERT(pContext != NULL);
18381 MA_ASSERT(pCaps != NULL);
18382 MA_ASSERT(pDeviceInfo != NULL);
18384 MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
18385 caps.dwFormats = pCaps->dwFormats;
18386 caps.wChannels = pCaps->wChannels;
18387 caps.NameGuid = pCaps->NameGuid;
18388 return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
18391 static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo)
18395 MA_ASSERT(pContext != NULL);
18396 MA_ASSERT(pCaps != NULL);
18397 MA_ASSERT(pDeviceInfo != NULL);
18399 MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
18400 caps.dwFormats = pCaps->dwFormats;
18401 caps.wChannels = pCaps->wChannels;
18402 caps.NameGuid = pCaps->NameGuid;
18403 return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
18407 static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
18409 UINT playbackDeviceCount;
18410 UINT captureDeviceCount;
18411 UINT iPlaybackDevice;
18412 UINT iCaptureDevice;
18414 MA_ASSERT(pContext != NULL);
18415 MA_ASSERT(callback != NULL);
18418 playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)();
18419 for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) {
18421 MA_WAVEOUTCAPS2A caps;
18423 MA_ZERO_OBJECT(&caps);
18425 result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (WAVEOUTCAPSA*)&caps, sizeof(caps));
18426 if (result == MMSYSERR_NOERROR) {
18427 ma_device_info deviceInfo;
18429 MA_ZERO_OBJECT(&deviceInfo);
18430 deviceInfo.id.winmm = iPlaybackDevice;
18432 /* The first enumerated device is the default device. */
18433 if (iPlaybackDevice == 0) {
18434 deviceInfo.isDefault = MA_TRUE;
18437 if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
18438 ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
18439 if (cbResult == MA_FALSE) {
18440 return MA_SUCCESS; /* Enumeration was stopped. */
18447 captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)();
18448 for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) {
18450 MA_WAVEINCAPS2A caps;
18452 MA_ZERO_OBJECT(&caps);
18454 result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (WAVEINCAPSA*)&caps, sizeof(caps));
18455 if (result == MMSYSERR_NOERROR) {
18456 ma_device_info deviceInfo;
18458 MA_ZERO_OBJECT(&deviceInfo);
18459 deviceInfo.id.winmm = iCaptureDevice;
18461 /* The first enumerated device is the default device. */
18462 if (iCaptureDevice == 0) {
18463 deviceInfo.isDefault = MA_TRUE;
18466 if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
18467 ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
18468 if (cbResult == MA_FALSE) {
18469 return MA_SUCCESS; /* Enumeration was stopped. */
18478 static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
18480 UINT winMMDeviceID;
18482 MA_ASSERT(pContext != NULL);
18485 if (pDeviceID != NULL) {
18486 winMMDeviceID = (UINT)pDeviceID->winmm;
18489 pDeviceInfo->id.winmm = winMMDeviceID;
18491 /* The first ID is the default device. */
18492 if (winMMDeviceID == 0) {
18493 pDeviceInfo->isDefault = MA_TRUE;
18496 if (deviceType == ma_device_type_playback) {
18498 MA_WAVEOUTCAPS2A caps;
18500 MA_ZERO_OBJECT(&caps);
18502 result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (WAVEOUTCAPSA*)&caps, sizeof(caps));
18503 if (result == MMSYSERR_NOERROR) {
18504 return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo);
18508 MA_WAVEINCAPS2A caps;
18510 MA_ZERO_OBJECT(&caps);
18512 result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (WAVEINCAPSA*)&caps, sizeof(caps));
18513 if (result == MMSYSERR_NOERROR) {
18514 return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo);
18518 return MA_NO_DEVICE;
18522 static ma_result ma_device_uninit__winmm(ma_device* pDevice)
18524 MA_ASSERT(pDevice != NULL);
18526 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
18527 ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture);
18528 CloseHandle((HANDLE)pDevice->winmm.hEventCapture);
18531 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
18532 ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
18533 ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
18534 CloseHandle((HANDLE)pDevice->winmm.hEventPlayback);
18537 ma__free_from_callbacks(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);
18539 MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */
18544 static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
18546 /* WinMM has a minimum period size of 40ms. */
18547 ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate);
18548 ma_uint32 periodSizeInFrames;
18550 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile);
18551 if (periodSizeInFrames < minPeriodSizeInFrames) {
18552 periodSizeInFrames = minPeriodSizeInFrames;
18555 return periodSizeInFrames;
18558 static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
18560 const char* errorMsg = "";
18561 ma_result errorCode = MA_ERROR;
18562 ma_result result = MA_SUCCESS;
18563 ma_uint32 heapSize;
18564 UINT winMMDeviceIDPlayback = 0;
18565 UINT winMMDeviceIDCapture = 0;
18567 MA_ASSERT(pDevice != NULL);
18569 MA_ZERO_OBJECT(&pDevice->winmm);
18571 if (pConfig->deviceType == ma_device_type_loopback) {
18572 return MA_DEVICE_TYPE_NOT_SUPPORTED;
18575 /* No exlusive mode with WinMM. */
18576 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
18577 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
18578 return MA_SHARE_MODE_NOT_SUPPORTED;
18581 if (pDescriptorPlayback->pDeviceID != NULL) {
18582 winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm;
18584 if (pDescriptorCapture->pDeviceID != NULL) {
18585 winMMDeviceIDCapture = (UINT)pDescriptorCapture->pDeviceID->winmm;
18588 /* The capture device needs to be initialized first. */
18589 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
18594 /* We use an event to know when a new fragment needs to be enqueued. */
18595 pDevice->winmm.hEventCapture = (ma_handle)CreateEventW(NULL, TRUE, TRUE, NULL);
18596 if (pDevice->winmm.hEventCapture == NULL) {
18597 errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError());
18601 /* The format should be based on the device's actual format. */
18602 if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MMSYSERR_NOERROR) {
18603 errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
18607 result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
18608 if (result != MA_SUCCESS) {
18609 errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
18613 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);
18614 if (resultMM != MMSYSERR_NOERROR) {
18615 errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
18619 pDescriptorCapture->format = ma_format_from_WAVEFORMATEX(&wf);
18620 pDescriptorCapture->channels = wf.nChannels;
18621 pDescriptorCapture->sampleRate = wf.nSamplesPerSec;
18622 ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
18623 pDescriptorCapture->periodCount = pDescriptorCapture->periodCount;
18624 pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
18627 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
18632 /* We use an event to know when a new fragment needs to be enqueued. */
18633 pDevice->winmm.hEventPlayback = (ma_handle)CreateEvent(NULL, TRUE, TRUE, NULL);
18634 if (pDevice->winmm.hEventPlayback == NULL) {
18635 errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError());
18639 /* The format should be based on the device's actual format. */
18640 if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MMSYSERR_NOERROR) {
18641 errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
18645 result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
18646 if (result != MA_SUCCESS) {
18647 errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
18651 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);
18652 if (resultMM != MMSYSERR_NOERROR) {
18653 errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
18657 pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX(&wf);
18658 pDescriptorPlayback->channels = wf.nChannels;
18659 pDescriptorPlayback->sampleRate = wf.nSamplesPerSec;
18660 ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
18661 pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount;
18662 pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
18666 The heap allocated data is allocated like so:
18668 [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer]
18671 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
18672 heapSize += sizeof(WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels));
18674 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
18675 heapSize += sizeof(WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels));
18678 pDevice->winmm._pHeapData = (ma_uint8*)ma__calloc_from_callbacks(heapSize, &pDevice->pContext->allocationCallbacks);
18679 if (pDevice->winmm._pHeapData == NULL) {
18680 errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY;
18684 MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize);
18686 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
18689 if (pConfig->deviceType == ma_device_type_capture) {
18690 pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
18691 pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount));
18693 pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
18694 pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount));
18697 /* Prepare headers. */
18698 for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) {
18699 ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels);
18701 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod));
18702 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes;
18703 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L;
18704 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L;
18705 ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
18708 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
18709 it's unlocked and available for writing. A value of 1 means it's locked.
18711 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0;
18715 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
18718 if (pConfig->deviceType == ma_device_type_playback) {
18719 pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData;
18720 pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*pDescriptorPlayback->periodCount);
18722 pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount));
18723 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));
18726 /* Prepare headers. */
18727 for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) {
18728 ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels);
18730 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod));
18731 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes;
18732 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L;
18733 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L;
18734 ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR));
18737 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
18738 it's unlocked and available for writing. A value of 1 means it's locked.
18740 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0;
18747 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
18748 if (pDevice->winmm.pWAVEHDRCapture != NULL) {
18750 for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) {
18751 ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
18755 ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture);
18758 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
18759 if (pDevice->winmm.pWAVEHDRCapture != NULL) {
18761 for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) {
18762 ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR));
18766 ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
18769 ma__free_from_callbacks(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);
18770 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, errorMsg, errorCode);
18773 static ma_result ma_device_start__winmm(ma_device* pDevice)
18775 MA_ASSERT(pDevice != NULL);
18777 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
18782 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
18784 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
18785 ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
18787 /* 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. */
18788 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
18789 resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
18790 if (resultMM != MMSYSERR_NOERROR) {
18791 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));
18794 /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */
18795 pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */
18798 /* Capture devices need to be explicitly started, unlike playback devices. */
18799 resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((HWAVEIN)pDevice->winmm.hDeviceCapture);
18800 if (resultMM != MMSYSERR_NOERROR) {
18801 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device.", ma_result_from_MMRESULT(resultMM));
18805 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
18806 /* Don't need to do anything for playback. It'll be started automatically in ma_device_start__winmm(). */
18812 static ma_result ma_device_stop__winmm(ma_device* pDevice)
18816 MA_ASSERT(pDevice != NULL);
18818 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
18819 if (pDevice->winmm.hDeviceCapture == NULL) {
18820 return MA_INVALID_ARGS;
18823 resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((HWAVEIN)pDevice->winmm.hDeviceCapture);
18824 if (resultMM != MMSYSERR_NOERROR) {
18825 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset capture device.", ma_result_from_MMRESULT(resultMM));
18829 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
18833 if (pDevice->winmm.hDevicePlayback == NULL) {
18834 return MA_INVALID_ARGS;
18837 /* 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. */
18838 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
18839 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) {
18840 if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */
18841 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
18842 break; /* An error occurred so just abandon ship and stop the device without draining. */
18845 pWAVEHDR[iPeriod].dwUser = 0;
18849 resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
18850 if (resultMM != MMSYSERR_NOERROR) {
18851 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset playback device.", ma_result_from_MMRESULT(resultMM));
18858 static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
18860 ma_result result = MA_SUCCESS;
18862 ma_uint32 totalFramesWritten;
18865 MA_ASSERT(pDevice != NULL);
18866 MA_ASSERT(pPCMFrames != NULL);
18868 if (pFramesWritten != NULL) {
18869 *pFramesWritten = 0;
18872 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
18874 /* Keep processing as much data as possible. */
18875 totalFramesWritten = 0;
18876 while (totalFramesWritten < frameCount) {
18877 /* If the current header has some space available we need to write part of it. */
18878 if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */
18880 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
18881 write it out and move on to the next iteration.
18883 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
18884 ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback;
18886 ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten));
18887 const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf);
18888 void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf);
18889 MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
18891 pDevice->winmm.headerFramesConsumedPlayback += framesToCopy;
18892 totalFramesWritten += framesToCopy;
18894 /* If we've consumed the buffer entirely we need to write it out to the device. */
18895 if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) {
18896 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */
18897 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
18899 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
18900 ResetEvent((HANDLE)pDevice->winmm.hEventPlayback);
18902 /* The device will be started here. */
18903 resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(WAVEHDR));
18904 if (resultMM != MMSYSERR_NOERROR) {
18905 result = ma_result_from_MMRESULT(resultMM);
18906 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed.", result);
18910 /* Make sure we move to the next header. */
18911 pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods;
18912 pDevice->winmm.headerFramesConsumedPlayback = 0;
18915 /* If at this point we have consumed the entire input buffer we can return. */
18916 MA_ASSERT(totalFramesWritten <= frameCount);
18917 if (totalFramesWritten == frameCount) {
18921 /* Getting here means there's more to process. */
18925 /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */
18926 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
18931 /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
18932 if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & WHDR_DONE) != 0) {
18933 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */
18934 pDevice->winmm.headerFramesConsumedPlayback = 0;
18937 /* If the device has been stopped we need to break. */
18938 if (ma_device_get_state(pDevice) != MA_STATE_STARTED) {
18943 if (pFramesWritten != NULL) {
18944 *pFramesWritten = totalFramesWritten;
18950 static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
18952 ma_result result = MA_SUCCESS;
18954 ma_uint32 totalFramesRead;
18957 MA_ASSERT(pDevice != NULL);
18958 MA_ASSERT(pPCMFrames != NULL);
18960 if (pFramesRead != NULL) {
18964 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
18966 /* Keep processing as much data as possible. */
18967 totalFramesRead = 0;
18968 while (totalFramesRead < frameCount) {
18969 /* If the current header has some space available we need to write part of it. */
18970 if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */
18971 /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */
18972 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
18973 ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture;
18975 ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead));
18976 const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf);
18977 void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf);
18978 MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
18980 pDevice->winmm.headerFramesConsumedCapture += framesToCopy;
18981 totalFramesRead += framesToCopy;
18983 /* If we've consumed the buffer entirely we need to add it back to the device. */
18984 if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) {
18985 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */
18986 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
18988 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
18989 ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
18991 /* The device will be started here. */
18992 resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(WAVEHDR));
18993 if (resultMM != MMSYSERR_NOERROR) {
18994 result = ma_result_from_MMRESULT(resultMM);
18995 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed.", result);
18999 /* Make sure we move to the next header. */
19000 pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods;
19001 pDevice->winmm.headerFramesConsumedCapture = 0;
19004 /* If at this point we have filled the entire input buffer we can return. */
19005 MA_ASSERT(totalFramesRead <= frameCount);
19006 if (totalFramesRead == frameCount) {
19010 /* Getting here means there's more to process. */
19014 /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */
19015 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
19020 /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
19021 if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & WHDR_DONE) != 0) {
19022 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */
19023 pDevice->winmm.headerFramesConsumedCapture = 0;
19026 /* If the device has been stopped we need to break. */
19027 if (ma_device_get_state(pDevice) != MA_STATE_STARTED) {
19032 if (pFramesRead != NULL) {
19033 *pFramesRead = totalFramesRead;
19039 static ma_result ma_context_uninit__winmm(ma_context* pContext)
19041 MA_ASSERT(pContext != NULL);
19042 MA_ASSERT(pContext->backend == ma_backend_winmm);
19044 ma_dlclose(pContext, pContext->winmm.hWinMM);
19048 static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
19050 MA_ASSERT(pContext != NULL);
19054 pContext->winmm.hWinMM = ma_dlopen(pContext, "winmm.dll");
19055 if (pContext->winmm.hWinMM == NULL) {
19056 return MA_NO_BACKEND;
19059 pContext->winmm.waveOutGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetNumDevs");
19060 pContext->winmm.waveOutGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetDevCapsA");
19061 pContext->winmm.waveOutOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutOpen");
19062 pContext->winmm.waveOutClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutClose");
19063 pContext->winmm.waveOutPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutPrepareHeader");
19064 pContext->winmm.waveOutUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutUnprepareHeader");
19065 pContext->winmm.waveOutWrite = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutWrite");
19066 pContext->winmm.waveOutReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutReset");
19067 pContext->winmm.waveInGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetNumDevs");
19068 pContext->winmm.waveInGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetDevCapsA");
19069 pContext->winmm.waveInOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInOpen");
19070 pContext->winmm.waveInClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInClose");
19071 pContext->winmm.waveInPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInPrepareHeader");
19072 pContext->winmm.waveInUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInUnprepareHeader");
19073 pContext->winmm.waveInAddBuffer = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInAddBuffer");
19074 pContext->winmm.waveInStart = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInStart");
19075 pContext->winmm.waveInReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInReset");
19077 pCallbacks->onContextInit = ma_context_init__winmm;
19078 pCallbacks->onContextUninit = ma_context_uninit__winmm;
19079 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm;
19080 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm;
19081 pCallbacks->onDeviceInit = ma_device_init__winmm;
19082 pCallbacks->onDeviceUninit = ma_device_uninit__winmm;
19083 pCallbacks->onDeviceStart = ma_device_start__winmm;
19084 pCallbacks->onDeviceStop = ma_device_stop__winmm;
19085 pCallbacks->onDeviceRead = ma_device_read__winmm;
19086 pCallbacks->onDeviceWrite = ma_device_write__winmm;
19087 pCallbacks->onDeviceDataLoop = NULL; /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */
19096 /******************************************************************************
19100 ******************************************************************************/
19103 #ifdef MA_NO_RUNTIME_LINKING
19105 /* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */
19106 #if !defined(__cplusplus)
19107 #if defined(__STRICT_ANSI__)
19108 #if !defined(inline)
19109 #define inline __inline__ __attribute__((always_inline))
19110 #define MA_INLINE_DEFINED
19114 #include <alsa/asoundlib.h>
19115 #if defined(MA_INLINE_DEFINED)
19117 #undef MA_INLINE_DEFINED
19120 typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t;
19121 typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t;
19122 typedef snd_pcm_stream_t ma_snd_pcm_stream_t;
19123 typedef snd_pcm_format_t ma_snd_pcm_format_t;
19124 typedef snd_pcm_access_t ma_snd_pcm_access_t;
19125 typedef snd_pcm_t ma_snd_pcm_t;
19126 typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
19127 typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
19128 typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
19129 typedef snd_pcm_info_t ma_snd_pcm_info_t;
19130 typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t;
19131 typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t;
19132 typedef snd_pcm_state_t ma_snd_pcm_state_t;
19134 /* snd_pcm_stream_t */
19135 #define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK
19136 #define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE
19138 /* snd_pcm_format_t */
19139 #define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN
19140 #define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8
19141 #define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE
19142 #define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE
19143 #define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE
19144 #define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE
19145 #define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE
19146 #define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE
19147 #define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE
19148 #define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE
19149 #define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE
19150 #define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE
19151 #define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW
19152 #define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW
19153 #define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE
19154 #define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE
19156 /* ma_snd_pcm_access_t */
19157 #define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED
19158 #define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED
19159 #define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX
19160 #define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED
19161 #define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED
19163 /* Channel positions. */
19164 #define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN
19165 #define MA_SND_CHMAP_NA SND_CHMAP_NA
19166 #define MA_SND_CHMAP_MONO SND_CHMAP_MONO
19167 #define MA_SND_CHMAP_FL SND_CHMAP_FL
19168 #define MA_SND_CHMAP_FR SND_CHMAP_FR
19169 #define MA_SND_CHMAP_RL SND_CHMAP_RL
19170 #define MA_SND_CHMAP_RR SND_CHMAP_RR
19171 #define MA_SND_CHMAP_FC SND_CHMAP_FC
19172 #define MA_SND_CHMAP_LFE SND_CHMAP_LFE
19173 #define MA_SND_CHMAP_SL SND_CHMAP_SL
19174 #define MA_SND_CHMAP_SR SND_CHMAP_SR
19175 #define MA_SND_CHMAP_RC SND_CHMAP_RC
19176 #define MA_SND_CHMAP_FLC SND_CHMAP_FLC
19177 #define MA_SND_CHMAP_FRC SND_CHMAP_FRC
19178 #define MA_SND_CHMAP_RLC SND_CHMAP_RLC
19179 #define MA_SND_CHMAP_RRC SND_CHMAP_RRC
19180 #define MA_SND_CHMAP_FLW SND_CHMAP_FLW
19181 #define MA_SND_CHMAP_FRW SND_CHMAP_FRW
19182 #define MA_SND_CHMAP_FLH SND_CHMAP_FLH
19183 #define MA_SND_CHMAP_FCH SND_CHMAP_FCH
19184 #define MA_SND_CHMAP_FRH SND_CHMAP_FRH
19185 #define MA_SND_CHMAP_TC SND_CHMAP_TC
19186 #define MA_SND_CHMAP_TFL SND_CHMAP_TFL
19187 #define MA_SND_CHMAP_TFR SND_CHMAP_TFR
19188 #define MA_SND_CHMAP_TFC SND_CHMAP_TFC
19189 #define MA_SND_CHMAP_TRL SND_CHMAP_TRL
19190 #define MA_SND_CHMAP_TRR SND_CHMAP_TRR
19191 #define MA_SND_CHMAP_TRC SND_CHMAP_TRC
19192 #define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC
19193 #define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC
19194 #define MA_SND_CHMAP_TSL SND_CHMAP_TSL
19195 #define MA_SND_CHMAP_TSR SND_CHMAP_TSR
19196 #define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE
19197 #define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE
19198 #define MA_SND_CHMAP_BC SND_CHMAP_BC
19199 #define MA_SND_CHMAP_BLC SND_CHMAP_BLC
19200 #define MA_SND_CHMAP_BRC SND_CHMAP_BRC
19202 /* Open mode flags. */
19203 #define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE
19204 #define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS
19205 #define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT
19207 #include <errno.h> /* For EPIPE, etc. */
19208 typedef unsigned long ma_snd_pcm_uframes_t;
19209 typedef long ma_snd_pcm_sframes_t;
19210 typedef int ma_snd_pcm_stream_t;
19211 typedef int ma_snd_pcm_format_t;
19212 typedef int ma_snd_pcm_access_t;
19213 typedef int ma_snd_pcm_state_t;
19214 typedef struct ma_snd_pcm_t ma_snd_pcm_t;
19215 typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
19216 typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
19217 typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
19218 typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t;
19222 unsigned int first;
19224 } ma_snd_pcm_channel_area_t;
19227 unsigned int channels;
19228 unsigned int pos[1];
19229 } ma_snd_pcm_chmap_t;
19231 /* snd_pcm_state_t */
19232 #define MA_SND_PCM_STATE_OPEN 0
19233 #define MA_SND_PCM_STATE_SETUP 1
19234 #define MA_SND_PCM_STATE_PREPARED 2
19235 #define MA_SND_PCM_STATE_RUNNING 3
19236 #define MA_SND_PCM_STATE_XRUN 4
19237 #define MA_SND_PCM_STATE_DRAINING 5
19238 #define MA_SND_PCM_STATE_PAUSED 6
19239 #define MA_SND_PCM_STATE_SUSPENDED 7
19240 #define MA_SND_PCM_STATE_DISCONNECTED 8
19242 /* snd_pcm_stream_t */
19243 #define MA_SND_PCM_STREAM_PLAYBACK 0
19244 #define MA_SND_PCM_STREAM_CAPTURE 1
19246 /* snd_pcm_format_t */
19247 #define MA_SND_PCM_FORMAT_UNKNOWN -1
19248 #define MA_SND_PCM_FORMAT_U8 1
19249 #define MA_SND_PCM_FORMAT_S16_LE 2
19250 #define MA_SND_PCM_FORMAT_S16_BE 3
19251 #define MA_SND_PCM_FORMAT_S24_LE 6
19252 #define MA_SND_PCM_FORMAT_S24_BE 7
19253 #define MA_SND_PCM_FORMAT_S32_LE 10
19254 #define MA_SND_PCM_FORMAT_S32_BE 11
19255 #define MA_SND_PCM_FORMAT_FLOAT_LE 14
19256 #define MA_SND_PCM_FORMAT_FLOAT_BE 15
19257 #define MA_SND_PCM_FORMAT_FLOAT64_LE 16
19258 #define MA_SND_PCM_FORMAT_FLOAT64_BE 17
19259 #define MA_SND_PCM_FORMAT_MU_LAW 20
19260 #define MA_SND_PCM_FORMAT_A_LAW 21
19261 #define MA_SND_PCM_FORMAT_S24_3LE 32
19262 #define MA_SND_PCM_FORMAT_S24_3BE 33
19264 /* snd_pcm_access_t */
19265 #define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0
19266 #define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1
19267 #define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2
19268 #define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3
19269 #define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4
19271 /* Channel positions. */
19272 #define MA_SND_CHMAP_UNKNOWN 0
19273 #define MA_SND_CHMAP_NA 1
19274 #define MA_SND_CHMAP_MONO 2
19275 #define MA_SND_CHMAP_FL 3
19276 #define MA_SND_CHMAP_FR 4
19277 #define MA_SND_CHMAP_RL 5
19278 #define MA_SND_CHMAP_RR 6
19279 #define MA_SND_CHMAP_FC 7
19280 #define MA_SND_CHMAP_LFE 8
19281 #define MA_SND_CHMAP_SL 9
19282 #define MA_SND_CHMAP_SR 10
19283 #define MA_SND_CHMAP_RC 11
19284 #define MA_SND_CHMAP_FLC 12
19285 #define MA_SND_CHMAP_FRC 13
19286 #define MA_SND_CHMAP_RLC 14
19287 #define MA_SND_CHMAP_RRC 15
19288 #define MA_SND_CHMAP_FLW 16
19289 #define MA_SND_CHMAP_FRW 17
19290 #define MA_SND_CHMAP_FLH 18
19291 #define MA_SND_CHMAP_FCH 19
19292 #define MA_SND_CHMAP_FRH 20
19293 #define MA_SND_CHMAP_TC 21
19294 #define MA_SND_CHMAP_TFL 22
19295 #define MA_SND_CHMAP_TFR 23
19296 #define MA_SND_CHMAP_TFC 24
19297 #define MA_SND_CHMAP_TRL 25
19298 #define MA_SND_CHMAP_TRR 26
19299 #define MA_SND_CHMAP_TRC 27
19300 #define MA_SND_CHMAP_TFLC 28
19301 #define MA_SND_CHMAP_TFRC 29
19302 #define MA_SND_CHMAP_TSL 30
19303 #define MA_SND_CHMAP_TSR 31
19304 #define MA_SND_CHMAP_LLFE 32
19305 #define MA_SND_CHMAP_RLFE 33
19306 #define MA_SND_CHMAP_BC 34
19307 #define MA_SND_CHMAP_BLC 35
19308 #define MA_SND_CHMAP_BRC 36
19310 /* Open mode flags. */
19311 #define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000
19312 #define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000
19313 #define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000
19316 typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode);
19317 typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm);
19318 typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void);
19319 typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
19320 typedef int (* ma_snd_pcm_hw_params_set_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val);
19321 typedef int (* ma_snd_pcm_hw_params_set_format_first_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format);
19322 typedef void (* ma_snd_pcm_hw_params_get_format_mask_proc) (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask);
19323 typedef int (* ma_snd_pcm_hw_params_set_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val);
19324 typedef int (* ma_snd_pcm_hw_params_set_channels_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val);
19325 typedef int (* ma_snd_pcm_hw_params_set_channels_minmax_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *minimum, unsigned int *maximum);
19326 typedef int (* ma_snd_pcm_hw_params_set_rate_resample_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val);
19327 typedef int (* ma_snd_pcm_hw_params_set_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir);
19328 typedef int (* ma_snd_pcm_hw_params_set_rate_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
19329 typedef int (* ma_snd_pcm_hw_params_set_buffer_size_near_proc)(ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val);
19330 typedef int (* ma_snd_pcm_hw_params_set_periods_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
19331 typedef int (* ma_snd_pcm_hw_params_set_access_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t _access);
19332 typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format);
19333 typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
19334 typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
19335 typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
19336 typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
19337 typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
19338 typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
19339 typedef int (* ma_snd_pcm_hw_params_get_buffer_size_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val);
19340 typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
19341 typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access);
19342 typedef int (* ma_snd_pcm_hw_params_test_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val);
19343 typedef int (* ma_snd_pcm_hw_params_test_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val);
19344 typedef int (* ma_snd_pcm_hw_params_test_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir);
19345 typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
19346 typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void);
19347 typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
19348 typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (const ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val);
19349 typedef int (* ma_snd_pcm_sw_params_set_avail_min_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val);
19350 typedef int (* ma_snd_pcm_sw_params_set_start_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val);
19351 typedef int (* ma_snd_pcm_sw_params_set_stop_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val);
19352 typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
19353 typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void);
19354 typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val);
19355 typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm);
19356 typedef ma_snd_pcm_state_t (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm);
19357 typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm);
19358 typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm);
19359 typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm);
19360 typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm);
19361 typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints);
19362 typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id);
19363 typedef int (* ma_snd_card_get_index_proc) (const char *name);
19364 typedef int (* ma_snd_device_name_free_hint_proc) (void **hints);
19365 typedef int (* ma_snd_pcm_mmap_begin_proc) (ma_snd_pcm_t *pcm, const ma_snd_pcm_channel_area_t **areas, ma_snd_pcm_uframes_t *offset, ma_snd_pcm_uframes_t *frames);
19366 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_mmap_commit_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_uframes_t offset, ma_snd_pcm_uframes_t frames);
19367 typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent);
19368 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size);
19369 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size);
19370 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm);
19371 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm);
19372 typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout);
19373 typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info);
19374 typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void);
19375 typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info);
19376 typedef int (* ma_snd_config_update_free_global_proc) (void);
19378 /* This array specifies each of the common devices that can be used for both playback and capture. */
19379 static const char* g_maCommonDeviceNamesALSA[] = {
19386 /* This array allows us to blacklist specific playback devices. */
19387 static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = {
19391 /* This array allows us to blacklist specific capture devices. */
19392 static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = {
19397 static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format)
19399 ma_snd_pcm_format_t ALSAFormats[] = {
19400 MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */
19401 MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */
19402 MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */
19403 MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */
19404 MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */
19405 MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */
19408 if (ma_is_big_endian()) {
19409 ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN;
19410 ALSAFormats[1] = MA_SND_PCM_FORMAT_U8;
19411 ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE;
19412 ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE;
19413 ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE;
19414 ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE;
19417 return ALSAFormats[format];
19420 static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA)
19422 if (ma_is_little_endian()) {
19423 switch (formatALSA) {
19424 case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16;
19425 case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24;
19426 case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32;
19427 case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32;
19431 switch (formatALSA) {
19432 case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16;
19433 case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24;
19434 case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32;
19435 case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32;
19440 /* Endian agnostic. */
19441 switch (formatALSA) {
19442 case MA_SND_PCM_FORMAT_U8: return ma_format_u8;
19443 default: return ma_format_unknown;
19447 static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos)
19449 switch (alsaChannelPos)
19451 case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO;
19452 case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT;
19453 case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT;
19454 case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT;
19455 case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT;
19456 case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER;
19457 case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE;
19458 case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT;
19459 case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT;
19460 case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER;
19461 case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER;
19462 case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER;
19463 case MA_SND_CHMAP_RLC: return 0;
19464 case MA_SND_CHMAP_RRC: return 0;
19465 case MA_SND_CHMAP_FLW: return 0;
19466 case MA_SND_CHMAP_FRW: return 0;
19467 case MA_SND_CHMAP_FLH: return 0;
19468 case MA_SND_CHMAP_FCH: return 0;
19469 case MA_SND_CHMAP_FRH: return 0;
19470 case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER;
19471 case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT;
19472 case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT;
19473 case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER;
19474 case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT;
19475 case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT;
19476 case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER;
19483 static ma_bool32 ma_is_common_device_name__alsa(const char* name)
19486 for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) {
19487 if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) {
19496 static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name)
19499 for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) {
19500 if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) {
19508 static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name)
19511 for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) {
19512 if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) {
19520 static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name)
19522 if (deviceType == ma_device_type_playback) {
19523 return ma_is_playback_device_blacklisted__alsa(name);
19525 return ma_is_capture_device_blacklisted__alsa(name);
19530 static const char* ma_find_char(const char* str, char c, int* index)
19534 if (str[i] == '\0') {
19535 if (index) *index = -1;
19540 if (index) *index = i;
19547 /* Should never get here, but treat it as though the character was not found to make me feel better inside. */
19548 if (index) *index = -1;
19552 static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid)
19554 /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */
19560 if (hwid == NULL) {
19564 if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') {
19570 dev = ma_find_char(hwid, ',', &commaPos);
19574 dev += 1; /* Skip past the ",". */
19577 /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */
19578 for (i = 0; i < commaPos; ++i) {
19579 if (hwid[i] < '0' || hwid[i] > '9') {
19584 /* Check if everything after the "," is numeric. If not, return false. */
19586 while (dev[i] != '\0') {
19587 if (dev[i] < '0' || dev[i] > '9') {
19596 static int ma_convert_device_name_to_hw_format__alsa(ma_context* pContext, char* dst, size_t dstSize, const char* src) /* Returns 0 on success, non-0 on error. */
19598 /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */
19610 return -1; /* Absolute minimum size of the output buffer is 7 bytes. */
19613 *dst = '\0'; /* Safety. */
19618 /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */
19619 if (ma_is_device_name_in_hw_format__alsa(src)) {
19620 return ma_strcpy_s(dst, dstSize, src);
19623 src = ma_find_char(src, ':', &colonPos);
19625 return -1; /* Couldn't find a colon */
19628 dev = ma_find_char(src, ',', &commaPos);
19631 ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */
19633 dev = dev + 5; /* +5 = ",DEV=" */
19634 ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */
19637 cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card);
19638 if (cardIndex < 0) {
19639 return -2; /* Failed to retrieve the card index. */
19642 /*printf("TESTING: CARD=%s,DEV=%s\n", card, dev); */
19645 /* Construction. */
19646 dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':';
19647 if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) {
19650 if (ma_strcat_s(dst, dstSize, ",") != 0) {
19653 if (ma_strcat_s(dst, dstSize, dev) != 0) {
19660 static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID)
19664 MA_ASSERT(pHWID != NULL);
19666 for (i = 0; i < count; ++i) {
19667 if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) {
19676 static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, int openMode, ma_snd_pcm_t** ppPCM)
19678 ma_snd_pcm_t* pPCM;
19679 ma_snd_pcm_stream_t stream;
19681 MA_ASSERT(pContext != NULL);
19682 MA_ASSERT(ppPCM != NULL);
19687 stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE;
19689 if (pDeviceID == NULL) {
19690 ma_bool32 isDeviceOpen;
19694 We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes
19695 me feel better to try as hard as we can get to get _something_ working.
19697 const char* defaultDeviceNames[] = {
19707 if (shareMode == ma_share_mode_exclusive) {
19708 defaultDeviceNames[1] = "hw";
19709 defaultDeviceNames[2] = "hw:0";
19710 defaultDeviceNames[3] = "hw:0,0";
19712 if (deviceType == ma_device_type_playback) {
19713 defaultDeviceNames[1] = "dmix";
19714 defaultDeviceNames[2] = "dmix:0";
19715 defaultDeviceNames[3] = "dmix:0,0";
19717 defaultDeviceNames[1] = "dsnoop";
19718 defaultDeviceNames[2] = "dsnoop:0";
19719 defaultDeviceNames[3] = "dsnoop:0,0";
19721 defaultDeviceNames[4] = "hw";
19722 defaultDeviceNames[5] = "hw:0";
19723 defaultDeviceNames[6] = "hw:0,0";
19726 isDeviceOpen = MA_FALSE;
19727 for (i = 0; i < ma_countof(defaultDeviceNames); ++i) {
19728 if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') {
19729 if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) {
19730 isDeviceOpen = MA_TRUE;
19736 if (!isDeviceOpen) {
19737 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);
19741 We're trying to open a specific device. There's a few things to consider here:
19743 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
19744 an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it
19745 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").
19748 /* May end up needing to make small adjustments to the ID, so make a copy. */
19749 ma_device_id deviceID = *pDeviceID;
19750 int resultALSA = -ENODEV;
19752 if (deviceID.alsa[0] != ':') {
19753 /* The ID is not in ":0,0" format. Use the ID exactly as-is. */
19754 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode);
19758 /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */
19759 if (deviceID.alsa[1] == '\0') {
19760 deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */
19763 if (shareMode == ma_share_mode_shared) {
19764 if (deviceType == ma_device_type_playback) {
19765 ma_strcpy_s(hwid, sizeof(hwid), "dmix");
19767 ma_strcpy_s(hwid, sizeof(hwid), "dsnoop");
19770 if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
19771 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
19775 /* 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. */
19776 if (resultALSA != 0) {
19777 ma_strcpy_s(hwid, sizeof(hwid), "hw");
19778 if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
19779 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
19784 if (resultALSA < 0) {
19785 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed.", ma_result_from_errno(-resultALSA));
19794 static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
19797 ma_bool32 cbResult = MA_TRUE;
19798 char** ppDeviceHints;
19799 ma_device_id* pUniqueIDs = NULL;
19800 ma_uint32 uniqueIDCount = 0;
19801 char** ppNextDeviceHint;
19803 MA_ASSERT(pContext != NULL);
19804 MA_ASSERT(callback != NULL);
19806 ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock);
19808 resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints);
19809 if (resultALSA < 0) {
19810 ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
19811 return ma_result_from_errno(-resultALSA);
19814 ppNextDeviceHint = ppDeviceHints;
19815 while (*ppNextDeviceHint != NULL) {
19816 char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME");
19817 char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC");
19818 char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID");
19819 ma_device_type deviceType = ma_device_type_playback;
19820 ma_bool32 stopEnumeration = MA_FALSE;
19821 char hwid[sizeof(pUniqueIDs->alsa)];
19822 ma_device_info deviceInfo;
19824 if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) {
19825 deviceType = ma_device_type_playback;
19827 if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) {
19828 deviceType = ma_device_type_capture;
19831 if (NAME != NULL) {
19832 if (pContext->alsa.useVerboseDeviceEnumeration) {
19833 /* Verbose mode. Use the name exactly as-is. */
19834 ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
19836 /* Simplified mode. Use ":%d,%d" format. */
19837 if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) {
19839 At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the
19840 plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device
19841 initialization time and is used as an indicator to try and use the most appropriate plugin depending on the
19842 device type and sharing mode.
19845 char* src = hwid+2;
19846 while ((*dst++ = *src++));
19848 /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */
19849 ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
19852 if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) {
19853 goto next_device; /* The device has already been enumerated. Move on to the next one. */
19855 /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */
19856 size_t oldCapacity = sizeof(*pUniqueIDs) * uniqueIDCount;
19857 size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1);
19858 ma_device_id* pNewUniqueIDs = (ma_device_id*)ma__realloc_from_callbacks(pUniqueIDs, newCapacity, oldCapacity, &pContext->allocationCallbacks);
19859 if (pNewUniqueIDs == NULL) {
19860 goto next_device; /* Failed to allocate memory. */
19863 pUniqueIDs = pNewUniqueIDs;
19864 MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid));
19865 uniqueIDCount += 1;
19869 MA_ZERO_MEMORY(hwid, sizeof(hwid));
19872 MA_ZERO_OBJECT(&deviceInfo);
19873 ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1);
19876 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
19877 just use the name of "default" as the indicator.
19879 if (ma_strcmp(deviceInfo.id.alsa, "default") == 0) {
19880 deviceInfo.isDefault = MA_TRUE;
19885 DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose
19886 device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish
19887 between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the
19890 The value in DESC seems to be split into two lines, with the first line being the name of the device and the
19891 second line being a description of the device. I don't like having the description be across two lines because
19892 it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line
19893 being put into parentheses. In simplified mode I'm just stripping the second line entirely.
19895 if (DESC != NULL) {
19897 const char* line2 = ma_find_char(DESC, '\n', &lfPos);
19898 if (line2 != NULL) {
19899 line2 += 1; /* Skip past the new-line character. */
19901 if (pContext->alsa.useVerboseDeviceEnumeration) {
19902 /* Verbose mode. Put the second line in brackets. */
19903 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
19904 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " (");
19905 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2);
19906 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")");
19908 /* Simplified mode. Strip the second line entirely. */
19909 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
19912 /* There's no second line. Just copy the whole description. */
19913 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1);
19917 if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) {
19918 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
19922 Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback
19923 again for the other device type in this case. We do this for known devices.
19926 if (ma_is_common_device_name__alsa(NAME)) {
19927 if (deviceType == ma_device_type_playback) {
19928 if (!ma_is_capture_device_blacklisted__alsa(NAME)) {
19929 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
19932 if (!ma_is_playback_device_blacklisted__alsa(NAME)) {
19933 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
19939 if (cbResult == MA_FALSE) {
19940 stopEnumeration = MA_TRUE;
19947 ppNextDeviceHint += 1;
19949 /* We need to stop enumeration if the callback returned false. */
19950 if (stopEnumeration) {
19955 ma__free_from_callbacks(pUniqueIDs, &pContext->allocationCallbacks);
19956 ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints);
19958 ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
19966 ma_device_type deviceType;
19967 const ma_device_id* pDeviceID;
19968 ma_share_mode shareMode;
19969 ma_device_info* pDeviceInfo;
19970 ma_bool32 foundDevice;
19971 } ma_context_get_device_info_enum_callback_data__alsa;
19973 static ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData)
19975 ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData;
19976 MA_ASSERT(pData != NULL);
19980 if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
19981 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
19982 pData->foundDevice = MA_TRUE;
19984 if (pData->deviceType == deviceType && (pData->pDeviceID != NULL && ma_strcmp(pData->pDeviceID->alsa, pDeviceInfo->id.alsa) == 0)) {
19985 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
19986 pData->foundDevice = MA_TRUE;
19990 /* Keep enumerating until we have found the device. */
19991 return !pData->foundDevice;
19994 static void ma_context_test_rate_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags, ma_device_info* pDeviceInfo)
19996 MA_ASSERT(pPCM != NULL);
19997 MA_ASSERT(pHWParams != NULL);
19998 MA_ASSERT(pDeviceInfo != NULL);
20000 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) {
20001 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
20002 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
20003 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
20004 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
20005 pDeviceInfo->nativeDataFormatCount += 1;
20009 static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 flags, ma_device_info* pDeviceInfo)
20011 ma_uint32 iSampleRate;
20012 unsigned int minSampleRate;
20013 unsigned int maxSampleRate;
20014 int sampleRateDir; /* Not used. Just passed into snd_pcm_hw_params_get_rate_min/max(). */
20016 /* There could be a range. */
20017 ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir);
20018 ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir);
20020 /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculus. */
20021 minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);
20022 maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);
20024 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) {
20025 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate];
20027 if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) {
20028 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo);
20032 /* Now make sure our min and max rates are included just in case they aren't in the range of our standard rates. */
20033 if (!ma_is_standard_sample_rate(minSampleRate)) {
20034 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo);
20037 if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) {
20038 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo);
20042 static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
20044 ma_context_get_device_info_enum_callback_data__alsa data;
20047 ma_snd_pcm_t* pPCM;
20048 ma_snd_pcm_hw_params_t* pHWParams;
20050 ma_uint32 iChannel;
20052 MA_ASSERT(pContext != NULL);
20054 /* We just enumerate to find basic information about the device. */
20055 data.deviceType = deviceType;
20056 data.pDeviceID = pDeviceID;
20057 data.pDeviceInfo = pDeviceInfo;
20058 data.foundDevice = MA_FALSE;
20059 result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data);
20060 if (result != MA_SUCCESS) {
20064 if (!data.foundDevice) {
20065 return MA_NO_DEVICE;
20068 if (ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
20069 pDeviceInfo->isDefault = MA_TRUE;
20072 /* For detailed info we need to open the device. */
20073 result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM);
20074 if (result != MA_SUCCESS) {
20078 /* We need to initialize a HW parameters object in order to know what formats are supported. */
20079 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);
20080 if (pHWParams == NULL) {
20081 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
20082 return MA_OUT_OF_MEMORY;
20085 resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
20086 if (resultALSA < 0) {
20087 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
20088 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
20089 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));
20093 Some ALSA devices can support many permutations of formats, channels and rates. We only support
20094 a fixed number of permutations which means we need to employ some strategies to ensure the best
20095 combinations are returned. An example is the "pulse" device which can do it's own data conversion
20096 in software and as a result can support any combination of format, channels and rate.
20098 We want to ensure the the first data formats are the best. We have a list of favored sample
20099 formats and sample rates, so these will be the basis of our iteration.
20102 /* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */
20103 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
20104 ma_format format = g_maFormatPriorities[iFormat];
20107 For each format we need to make sure we reset the configuration space so we don't return
20108 channel counts and rates that aren't compatible with a format.
20110 ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
20112 /* Test the format first. If this fails it means the format is not supported and we can skip it. */
20113 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) {
20114 /* The format is supported. */
20115 unsigned int minChannels;
20116 unsigned int maxChannels;
20119 The configuration space needs to be restricted to this format so we can get an accurate
20120 picture of which sample rates and channel counts are support with this format.
20122 ((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));
20124 /* Now we need to check for supported channels. */
20125 ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels);
20126 ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels);
20128 if (minChannels > MA_MAX_CHANNELS) {
20129 continue; /* Too many channels. */
20131 if (maxChannels < MA_MIN_CHANNELS) {
20132 continue; /* Not enough channels. */
20136 Make sure the channel count is clamped. This is mainly intended for the max channels
20137 because some devices can report an unbound maximum.
20139 minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
20140 maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
20142 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
20143 /* 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. */
20144 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. */
20146 /* The device only supports a specific set of channels. We need to iterate over all of them. */
20147 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
20148 /* Test the channel before applying it to the configuration space. */
20149 unsigned int channels = iChannel;
20151 /* Make sure our channel range is reset before testing again or else we'll always fail the test. */
20152 ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
20153 ((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));
20155 if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) {
20156 /* The channel count is supported. */
20158 /* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */
20159 ((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels);
20161 /* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */
20162 ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo);
20164 /* The channel count is not supported. Skip. */
20169 /* The format is not supported. Skip. */
20173 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
20175 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
20179 static ma_result ma_device_uninit__alsa(ma_device* pDevice)
20181 MA_ASSERT(pDevice != NULL);
20183 if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) {
20184 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
20187 if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) {
20188 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
20194 static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
20198 ma_snd_pcm_t* pPCM;
20199 ma_bool32 isUsingMMap;
20200 ma_snd_pcm_format_t formatALSA;
20201 ma_format internalFormat;
20202 ma_uint32 internalChannels;
20203 ma_uint32 internalSampleRate;
20204 ma_channel internalChannelMap[MA_MAX_CHANNELS];
20205 ma_uint32 internalPeriodSizeInFrames;
20206 ma_uint32 internalPeriods;
20208 ma_snd_pcm_hw_params_t* pHWParams;
20209 ma_snd_pcm_sw_params_t* pSWParams;
20210 ma_snd_pcm_uframes_t bufferBoundary;
20212 MA_ASSERT(pConfig != NULL);
20213 MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */
20214 MA_ASSERT(pDevice != NULL);
20216 formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format);
20219 if (pConfig->alsa.noAutoResample) {
20220 openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE;
20222 if (pConfig->alsa.noAutoChannels) {
20223 openMode |= MA_SND_PCM_NO_AUTO_CHANNELS;
20225 if (pConfig->alsa.noAutoFormat) {
20226 openMode |= MA_SND_PCM_NO_AUTO_FORMAT;
20229 result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM);
20230 if (result != MA_SUCCESS) {
20235 /* Hardware parameters. */
20236 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);
20237 if (pHWParams == NULL) {
20238 return MA_OUT_OF_MEMORY;
20241 resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
20242 if (resultALSA < 0) {
20243 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20244 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20245 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));
20248 /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */
20249 isUsingMMap = MA_FALSE;
20250 #if 0 /* NOTE: MMAP mode temporarily disabled. */
20251 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. */
20252 if (!pConfig->alsa.noMMap && ma_device__is_async(pDevice)) {
20253 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) {
20254 pDevice->alsa.isUsingMMap = MA_TRUE;
20260 if (!isUsingMMap) {
20261 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);
20262 if (resultALSA < 0) {
20263 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20264 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20265 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));
20270 Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't
20271 find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS.
20277 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
20278 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.
20280 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) {
20281 /* We're either requesting the native format or the specified format is not supported. */
20284 formatALSA = MA_SND_PCM_FORMAT_UNKNOWN;
20285 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) {
20286 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) {
20287 formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]);
20292 if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) {
20293 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20294 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20295 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);
20299 resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA);
20300 if (resultALSA < 0) {
20301 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20302 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20303 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));
20306 internalFormat = ma_format_from_alsa(formatALSA);
20307 if (internalFormat == ma_format_unknown) {
20308 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20309 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20310 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
20316 unsigned int channels = pDescriptor->channels;
20317 if (channels == 0) {
20318 channels = MA_DEFAULT_CHANNELS;
20321 resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels);
20322 if (resultALSA < 0) {
20323 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20324 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20325 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));
20328 internalChannels = (ma_uint32)channels;
20333 unsigned int sampleRate;
20336 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
20337 problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable
20340 To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a
20341 sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling
20342 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
20345 miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine
20346 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
20347 good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion.
20349 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
20350 this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins.
20352 ((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0);
20354 sampleRate = pDescriptor->sampleRate;
20355 if (sampleRate == 0) {
20356 sampleRate = MA_DEFAULT_SAMPLE_RATE;
20359 resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0);
20360 if (resultALSA < 0) {
20361 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20362 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20363 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));
20366 internalSampleRate = (ma_uint32)sampleRate;
20371 ma_uint32 periods = pDescriptor->periodCount;
20373 resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL);
20374 if (resultALSA < 0) {
20375 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20376 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20377 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));
20380 internalPeriods = periods;
20385 ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods;
20387 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);
20388 if (resultALSA < 0) {
20389 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20390 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20391 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));
20394 internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods;
20397 /* Apply hardware parameters. */
20398 resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams);
20399 if (resultALSA < 0) {
20400 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20401 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20402 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));
20405 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20409 /* Software parameters. */
20410 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);
20411 if (pSWParams == NULL) {
20412 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20413 return MA_OUT_OF_MEMORY;
20416 resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams);
20417 if (resultALSA < 0) {
20418 ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks);
20419 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20420 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));
20423 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));
20424 if (resultALSA < 0) {
20425 ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks);
20426 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20427 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.", ma_result_from_errno(-resultALSA));
20430 resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary);
20431 if (resultALSA < 0) {
20432 bufferBoundary = internalPeriodSizeInFrames * internalPeriods;
20435 /*printf("TRACE: bufferBoundary=%ld\n", bufferBoundary);*/
20437 if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */
20439 Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to
20440 the size of a period. But for full-duplex we need to set it such that it is at least two periods.
20442 resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2);
20443 if (resultALSA < 0) {
20444 ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks);
20445 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20446 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));
20449 resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary);
20450 if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */
20451 ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks);
20452 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20453 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));
20457 resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams);
20458 if (resultALSA < 0) {
20459 ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks);
20460 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20461 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));
20464 ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks);
20468 /* 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. */
20470 ma_snd_pcm_chmap_t* pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM);
20471 if (pChmap != NULL) {
20472 ma_uint32 iChannel;
20474 /* 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(). */
20475 if (pChmap->channels >= internalChannels) {
20476 /* Drop excess channels. */
20477 for (iChannel = 0; iChannel < internalChannels; ++iChannel) {
20478 internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
20484 Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate
20485 channels. If validation fails, fall back to defaults.
20487 ma_bool32 isValid = MA_TRUE;
20489 /* Fill with defaults. */
20490 ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
20492 /* Overwrite first pChmap->channels channels. */
20493 for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) {
20494 internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
20498 for (i = 0; i < internalChannels && isValid; ++i) {
20500 for (j = i+1; j < internalChannels; ++j) {
20501 if (internalChannelMap[i] == internalChannelMap[j]) {
20502 isValid = MA_FALSE;
20508 /* If our channel map is invalid, fall back to defaults. */
20510 ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
20517 /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */
20518 ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
20523 /* We're done. Prepare the device. */
20524 resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM);
20525 if (resultALSA < 0) {
20526 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20527 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.", ma_result_from_errno(-resultALSA));
20531 if (deviceType == ma_device_type_capture) {
20532 pDevice->alsa.pPCMCapture = (ma_ptr)pPCM;
20533 pDevice->alsa.isUsingMMapCapture = isUsingMMap;
20535 pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM;
20536 pDevice->alsa.isUsingMMapPlayback = isUsingMMap;
20539 pDescriptor->format = internalFormat;
20540 pDescriptor->channels = internalChannels;
20541 pDescriptor->sampleRate = internalSampleRate;
20542 ma_channel_map_copy(pDescriptor->channelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS));
20543 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
20544 pDescriptor->periodCount = internalPeriods;
20546 /*printf("format=%d; channels=%d; sampleRate=%d; periodSizeInFrames=%d; periodCount=%d\n", internalFormat, internalChannels, internalSampleRate, internalPeriodSizeInFrames, internalPeriods);*/
20551 static ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
20553 MA_ASSERT(pDevice != NULL);
20555 MA_ZERO_OBJECT(&pDevice->alsa);
20557 if (pConfig->deviceType == ma_device_type_loopback) {
20558 return MA_DEVICE_TYPE_NOT_SUPPORTED;
20561 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
20562 ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
20563 if (result != MA_SUCCESS) {
20568 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
20569 ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
20570 if (result != MA_SUCCESS) {
20578 static ma_result ma_device_start__alsa(ma_device* pDevice)
20582 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
20583 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
20584 if (resultALSA < 0) {
20585 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device.", ma_result_from_errno(-resultALSA));
20589 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
20590 /* Don't need to do anything for playback because it'll be started automatically when enough data has been written. */
20596 static ma_result ma_device_stop__alsa(ma_device* pDevice)
20598 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
20599 ((ma_snd_pcm_drain_proc)pDevice->pContext->alsa.snd_pcm_drain)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
20601 /* We need to prepare the device again, otherwise we won't be able to restart the device. */
20602 if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
20603 #ifdef MA_DEBUG_OUTPUT
20604 printf("[ALSA] Failed to prepare capture device after stopping.\n");
20609 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
20610 ((ma_snd_pcm_drain_proc)pDevice->pContext->alsa.snd_pcm_drain)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
20612 /* We need to prepare the device again, otherwise we won't be able to restart the device. */
20613 if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) {
20614 #ifdef MA_DEBUG_OUTPUT
20615 printf("[ALSA] Failed to prepare playback device after stopping.\n");
20623 static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead)
20625 ma_snd_pcm_sframes_t resultALSA;
20627 MA_ASSERT(pDevice != NULL);
20628 MA_ASSERT(pFramesOut != NULL);
20630 if (pFramesRead != NULL) {
20635 resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
20636 if (resultALSA >= 0) {
20637 break; /* Success. */
20639 if (resultALSA == -EAGAIN) {
20640 /*printf("TRACE: EGAIN (read)\n");*/
20641 continue; /* Try again. */
20642 } else if (resultALSA == -EPIPE) {
20643 #if defined(MA_DEBUG_OUTPUT)
20644 printf("TRACE: EPIPE (read)\n");
20647 /* Overrun. Recover and try again. If this fails we need to return an error. */
20648 resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE);
20649 if (resultALSA < 0) {
20650 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.", ma_result_from_errno((int)-resultALSA));
20653 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
20654 if (resultALSA < 0) {
20655 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", ma_result_from_errno((int)-resultALSA));
20658 resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
20659 if (resultALSA < 0) {
20660 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to read data from the internal device.", ma_result_from_errno((int)-resultALSA));
20666 if (pFramesRead != NULL) {
20667 *pFramesRead = resultALSA;
20673 static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
20675 ma_snd_pcm_sframes_t resultALSA;
20677 MA_ASSERT(pDevice != NULL);
20678 MA_ASSERT(pFrames != NULL);
20680 if (pFramesWritten != NULL) {
20681 *pFramesWritten = 0;
20685 resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);
20686 if (resultALSA >= 0) {
20687 break; /* Success. */
20689 if (resultALSA == -EAGAIN) {
20690 /*printf("TRACE: EGAIN (write)\n");*/
20691 continue; /* Try again. */
20692 } else if (resultALSA == -EPIPE) {
20693 #if defined(MA_DEBUG_OUTPUT)
20694 printf("TRACE: EPIPE (write)\n");
20697 /* Underrun. Recover and try again. If this fails we need to return an error. */
20698 resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE);
20699 if (resultALSA < 0) { /* MA_TRUE=silent (don't print anything on error). */
20700 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.", ma_result_from_errno((int)-resultALSA));
20704 In my testing I have had a situation where writei() does not automatically restart the device even though I've set it
20705 up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of
20706 frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure
20707 if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't
20710 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
20711 if (resultALSA < 0) {
20712 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", ma_result_from_errno((int)-resultALSA));
20715 resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);
20716 if (resultALSA < 0) {
20717 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to write data to device after underrun.", ma_result_from_errno((int)-resultALSA));
20723 if (pFramesWritten != NULL) {
20724 *pFramesWritten = resultALSA;
20730 static ma_result ma_context_uninit__alsa(ma_context* pContext)
20732 MA_ASSERT(pContext != NULL);
20733 MA_ASSERT(pContext->backend == ma_backend_alsa);
20735 /* Clean up memory for memory leak checkers. */
20736 ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)();
20738 #ifndef MA_NO_RUNTIME_LINKING
20739 ma_dlclose(pContext, pContext->alsa.asoundSO);
20742 ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock);
20747 static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
20749 #ifndef MA_NO_RUNTIME_LINKING
20750 const char* libasoundNames[] = {
20756 for (i = 0; i < ma_countof(libasoundNames); ++i) {
20757 pContext->alsa.asoundSO = ma_dlopen(pContext, libasoundNames[i]);
20758 if (pContext->alsa.asoundSO != NULL) {
20763 if (pContext->alsa.asoundSO == NULL) {
20764 #ifdef MA_DEBUG_OUTPUT
20765 printf("[ALSA] Failed to open shared object.\n");
20767 return MA_NO_BACKEND;
20770 pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_open");
20771 pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_close");
20772 pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof");
20773 pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_any");
20774 pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format");
20775 pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first");
20776 pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask");
20777 pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels");
20778 pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near");
20779 pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax");
20780 pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample");
20781 pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate");
20782 pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near");
20783 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");
20784 pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near");
20785 pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access");
20786 pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format");
20787 pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels");
20788 pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min");
20789 pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max");
20790 pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate");
20791 pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min");
20792 pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max");
20793 pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size");
20794 pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods");
20795 pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access");
20796 pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format");
20797 pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels");
20798 pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate");
20799 pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params");
20800 pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof");
20801 pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_current");
20802 pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary");
20803 pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min");
20804 pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold");
20805 pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold");
20806 pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params");
20807 pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof");
20808 pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_test");
20809 pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_get_chmap");
20810 pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_state");
20811 pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_prepare");
20812 pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_start");
20813 pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drop");
20814 pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drain");
20815 pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_hint");
20816 pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_get_hint");
20817 pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_card_get_index");
20818 pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_free_hint");
20819 pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_begin");
20820 pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_commit");
20821 pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_recover");
20822 pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_readi");
20823 pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_writei");
20824 pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail");
20825 pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail_update");
20826 pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_wait");
20827 pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info");
20828 pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_sizeof");
20829 pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_get_name");
20830 pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_config_update_free_global");
20832 /* The system below is just for type safety. */
20833 ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open;
20834 ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close;
20835 ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof;
20836 ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any;
20837 ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format;
20838 ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first;
20839 ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask;
20840 ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels;
20841 ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near;
20842 ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample;
20843 ma_snd_pcm_hw_params_set_rate_near _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate;
20844 ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near;
20845 ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax;
20846 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;
20847 ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near;
20848 ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access;
20849 ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format;
20850 ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels;
20851 ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min;
20852 ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max;
20853 ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate;
20854 ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min;
20855 ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max;
20856 ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size;
20857 ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods;
20858 ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access;
20859 ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format;
20860 ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels;
20861 ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate;
20862 ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params;
20863 ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof;
20864 ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current;
20865 ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary;
20866 ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min;
20867 ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold;
20868 ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold;
20869 ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params;
20870 ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof;
20871 ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test;
20872 ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap;
20873 ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state;
20874 ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare;
20875 ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start;
20876 ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop;
20877 ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain;
20878 ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint;
20879 ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint;
20880 ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index;
20881 ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint;
20882 ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin;
20883 ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit;
20884 ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover;
20885 ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi;
20886 ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei;
20887 ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail;
20888 ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update;
20889 ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait;
20890 ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info;
20891 ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof;
20892 ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name;
20893 ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global;
20895 pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open;
20896 pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close;
20897 pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof;
20898 pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any;
20899 pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format;
20900 pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first;
20901 pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask;
20902 pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)_snd_pcm_hw_params_set_channels;
20903 pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near;
20904 pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)_snd_pcm_hw_params_set_channels_minmax;
20905 pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample;
20906 pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate;
20907 pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near;
20908 pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near;
20909 pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near;
20910 pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access;
20911 pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format;
20912 pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels;
20913 pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min;
20914 pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max;
20915 pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate;
20916 pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)_snd_pcm_hw_params_get_rate_min;
20917 pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)_snd_pcm_hw_params_get_rate_max;
20918 pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size;
20919 pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods;
20920 pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access;
20921 pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)_snd_pcm_hw_params_test_format;
20922 pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)_snd_pcm_hw_params_test_channels;
20923 pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)_snd_pcm_hw_params_test_rate;
20924 pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params;
20925 pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof;
20926 pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current;
20927 pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary;
20928 pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min;
20929 pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold;
20930 pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold;
20931 pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params;
20932 pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof;
20933 pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test;
20934 pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap;
20935 pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state;
20936 pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare;
20937 pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start;
20938 pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop;
20939 pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain;
20940 pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint;
20941 pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint;
20942 pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index;
20943 pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint;
20944 pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin;
20945 pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit;
20946 pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover;
20947 pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi;
20948 pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei;
20949 pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail;
20950 pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update;
20951 pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait;
20952 pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info;
20953 pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof;
20954 pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name;
20955 pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global;
20958 pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration;
20960 if (ma_mutex_init(&pContext->alsa.internalDeviceEnumLock) != MA_SUCCESS) {
20961 ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration.", MA_ERROR);
20964 pCallbacks->onContextInit = ma_context_init__alsa;
20965 pCallbacks->onContextUninit = ma_context_uninit__alsa;
20966 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa;
20967 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa;
20968 pCallbacks->onDeviceInit = ma_device_init__alsa;
20969 pCallbacks->onDeviceUninit = ma_device_uninit__alsa;
20970 pCallbacks->onDeviceStart = ma_device_start__alsa;
20971 pCallbacks->onDeviceStop = ma_device_stop__alsa;
20972 pCallbacks->onDeviceRead = ma_device_read__alsa;
20973 pCallbacks->onDeviceWrite = ma_device_write__alsa;
20974 pCallbacks->onDeviceDataLoop = NULL;
20982 /******************************************************************************
20986 ******************************************************************************/
20987 #ifdef MA_HAS_PULSEAUDIO
20989 The PulseAudio API, along with Apple's Core Audio, is the worst of the maintream audio APIs. This is a brief description of what's going on
20990 in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion.
20992 PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it
20993 allows you to enumerate over devices, nor does it seem to support the ability to stop and start streams. Looking at the documentation, it
20994 appears as though the stream is constantly running and you prevent sound from being emitted or captured by simply not calling the read or
20995 write functions. This is not a professional solution as it would be much better to *actually* stop the underlying stream. Perhaps the
20996 simple API has some smarts to do this automatically, but I'm not sure. Another limitation with the simple API is that it seems inefficient
20997 when you want to have multiple streams to a single context. For these reasons, miniaudio is not using the simple API.
20999 Since we're not using the simple API, we're left with the asynchronous API as our only other option. And boy, is this where it starts to
21000 get fun, and I don't mean that in a good way...
21002 The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands
21003 don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is
21004 enabled through the use of a "main loop". In the asychronous API you cannot get away from the main loop, and the main loop is where almost
21005 all of PulseAudio's problems stem from.
21007 When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own
21008 vtable, but it's much easier to just use one of the built-in main loop implementations. There's two generic implementations called
21009 pa_mainloop and pa_threaded_mainloop, and another implementation specific to GLib called pa_glib_mainloop. We're using pa_threaded_mainloop
21010 because it simplifies management of the worker thread. The idea of the main loop object is pretty self explanatory - you're supposed to use
21011 it to implement a worker thread which runs in a loop. The main loop is where operations are actually executed.
21013 To initialize the main loop, you just use `pa_threaded_mainloop_new()`. This is the first function you'll call. You can then get a pointer
21014 to the vtable with `pa_threaded_mainloop_get_api()` (the main loop vtable is called `pa_mainloop_api`). Again, you can bypass the threaded
21015 main loop object entirely and just implement `pa_mainloop_api` directly, but there's no need for it unless you're doing something extremely
21016 specialized such as if you want to integrate it into your application's existing main loop infrastructure.
21018 (EDIT 2021-01-26: miniaudio is no longer using `pa_threaded_mainloop` due to this issue: https://github.com/mackron/miniaudio/issues/262.
21019 It is now using `pa_mainloop` which turns out to be a simpler solution anyway. The rest of this rant still applies, however.)
21021 Once you have your main loop vtable (the `pa_mainloop_api` object) you can create the PulseAudio context. This is very similar to
21022 miniaudio's context and they map to each other quite well. You have one context to many streams, which is basically the same as miniaudio's
21023 one `ma_context` to many `ma_device`s. Here's where it starts to get annoying, however. When you first create the PulseAudio context, which
21024 is done with `pa_context_new()`, it's not actually connected to anything. When you connect, you call `pa_context_connect()`. However, if
21025 you remember, PulseAudio is an asynchronous API. That means you cannot just assume the context is connected after `pa_context_context()`
21026 has returned. You instead need to wait for it to connect. To do this, you need to either wait for a callback to get fired, which you can
21027 set with `pa_context_set_state_callback()`, or you can continuously poll the context's state. Either way, you need to run this in a loop.
21028 All objects from here out are created from the context, and, I believe, you can't be creating these objects until the context is connected.
21029 This waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before
21030 attempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`.
21032 The reason for this asynchronous design is to support cases where you're connecting to a remote server, say through a local network or an
21033 internet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local "server" running on the
21034 host machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind.
21036 Once the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device.
21037 The initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call
21038 `pa_stream_new()` to actually create it. Here is where we start to get into "operations". When configuring the stream, you can get
21039 information about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object
21040 is returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to
21041 run a loop (again!) to wait for the operation to complete which you can determine via a callback or polling, just like we did with the
21042 context. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up.
21043 All of that just to retrieve basic information about a device!
21045 Once the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the
21046 context, this needs to be connected. But we need to be careful here, because we're now about to introduce one of the most horrific design
21047 choices in PulseAudio.
21049 PulseAudio allows you to specify a callback that is fired when data can be written to or read from a stream. The language is important here
21050 because PulseAudio takes it literally, specifically the "can be". You would think these callbacks would be appropriate as the place for
21051 writing and reading data to and from the stream, and that would be right, except when it's not. When you initialize the stream, you can
21052 set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices
21053 straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified,
21054 PulseAudio will immediately fire it's write or read callback. This is *technically* correct (based on the wording in the documentation)
21055 because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback
21056 would be where a program will want to write or read data to or from the stream, but when it's called before the application has even
21057 requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at
21058 that point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the
21059 stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data
21060 callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio
21061 doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been
21062 started. The device state is used for this - if the state is anything other than `MA_STATE_STARTING` or `MA_STATE_STARTED`, the main data
21063 callback is not fired.
21065 This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will
21066 continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device
21067 is running, and will be fired regardless of whether or not the user actually wrote anything to the device/stream. This not the case in
21068 PulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call
21069 `pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always
21070 writing data, and if you don't have anything to write, just write silence. That's fine until you want to drain the stream. You see, if
21071 you're continuously writing data to the stream, the stream will never get drained! That means in order to drain the stream, you need to
21072 *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
21073 important? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained
21074 before returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write
21075 data to the stream (or else it won't ever complete draining), but not writing to the stream means the callback won't get fired again!
21077 This becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not*
21078 write anything to the stream. But then, since we didn't write anything to it, the write callback will *never* get called again if we just
21079 resume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This
21080 disconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the
21081 callback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.)
21083 Once you've created the stream, you need to connect it which involves the whole waiting procedure. This is the same process as the context,
21084 only this time you'll poll for the state with `pa_stream_get_status()`. The starting and stopping of a streaming is referred to as
21085 "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
21086 it's silly - why would you not just call it "starting" and "stopping" like any other normal audio API? Anyway, the act of corking is, you
21087 guessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is
21088 absolutely beyond me. Would it really be that hard to just make it run synchronously?
21090 Teardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that
21091 they were initialized in.
21093 That's about it from the PulseAudio side. A bit ranty, I know, but they really need to fix that main loop and callback system. They're
21094 embarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to
21095 run asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche
21096 requirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is
21097 constantly fired, regardless of whether or not `pa_stream_write()` has been called, and it needs to take a pointer to a buffer as a
21098 parameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These
21099 changes alone will change PulseAudio from one of the worst audio APIs to one of the best.
21104 It is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header
21105 to check for type safety. We cannot do this when linking at run time because the header might not be available.
21107 #ifdef MA_NO_RUNTIME_LINKING
21109 /* pulseaudio.h marks some functions with "inline" which isn't always supported. Need to emulate it. */
21110 #if !defined(__cplusplus)
21111 #if defined(__STRICT_ANSI__)
21112 #if !defined(inline)
21113 #define inline __inline__ __attribute__((always_inline))
21114 #define MA_INLINE_DEFINED
21118 #include <pulse/pulseaudio.h>
21119 #if defined(MA_INLINE_DEFINED)
21121 #undef MA_INLINE_DEFINED
21124 #define MA_PA_OK PA_OK
21125 #define MA_PA_ERR_ACCESS PA_ERR_ACCESS
21126 #define MA_PA_ERR_INVALID PA_ERR_INVALID
21127 #define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY
21129 #define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX
21130 #define MA_PA_RATE_MAX PA_RATE_MAX
21132 typedef pa_context_flags_t ma_pa_context_flags_t;
21133 #define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS
21134 #define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN
21135 #define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL
21137 typedef pa_stream_flags_t ma_pa_stream_flags_t;
21138 #define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS
21139 #define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED
21140 #define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING
21141 #define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC
21142 #define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE
21143 #define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS
21144 #define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS
21145 #define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT
21146 #define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE
21147 #define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS
21148 #define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE
21149 #define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE
21150 #define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT
21151 #define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED
21152 #define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY
21153 #define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS
21154 #define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
21155 #define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED
21156 #define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
21157 #define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
21158 #define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
21160 typedef pa_sink_flags_t ma_pa_sink_flags_t;
21161 #define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS
21162 #define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL
21163 #define MA_PA_SINK_LATENCY PA_SINK_LATENCY
21164 #define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE
21165 #define MA_PA_SINK_NETWORK PA_SINK_NETWORK
21166 #define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL
21167 #define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
21168 #define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME
21169 #define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY
21170 #define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS
21172 typedef pa_source_flags_t ma_pa_source_flags_t;
21173 #define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS
21174 #define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL
21175 #define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY
21176 #define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE
21177 #define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK
21178 #define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL
21179 #define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME
21180 #define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY
21181 #define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME
21183 typedef pa_context_state_t ma_pa_context_state_t;
21184 #define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED
21185 #define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING
21186 #define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING
21187 #define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME
21188 #define MA_PA_CONTEXT_READY PA_CONTEXT_READY
21189 #define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED
21190 #define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED
21192 typedef pa_stream_state_t ma_pa_stream_state_t;
21193 #define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED
21194 #define MA_PA_STREAM_CREATING PA_STREAM_CREATING
21195 #define MA_PA_STREAM_READY PA_STREAM_READY
21196 #define MA_PA_STREAM_FAILED PA_STREAM_FAILED
21197 #define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED
21199 typedef pa_operation_state_t ma_pa_operation_state_t;
21200 #define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING
21201 #define MA_PA_OPERATION_DONE PA_OPERATION_DONE
21202 #define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED
21204 typedef pa_sink_state_t ma_pa_sink_state_t;
21205 #define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE
21206 #define MA_PA_SINK_RUNNING PA_SINK_RUNNING
21207 #define MA_PA_SINK_IDLE PA_SINK_IDLE
21208 #define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED
21210 typedef pa_source_state_t ma_pa_source_state_t;
21211 #define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE
21212 #define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING
21213 #define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE
21214 #define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED
21216 typedef pa_seek_mode_t ma_pa_seek_mode_t;
21217 #define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE
21218 #define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE
21219 #define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ
21220 #define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END
21222 typedef pa_channel_position_t ma_pa_channel_position_t;
21223 #define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID
21224 #define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO
21225 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT
21226 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT
21227 #define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER
21228 #define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER
21229 #define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT
21230 #define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT
21231 #define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE
21232 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
21233 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
21234 #define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT
21235 #define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT
21236 #define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0
21237 #define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1
21238 #define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2
21239 #define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3
21240 #define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4
21241 #define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5
21242 #define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6
21243 #define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7
21244 #define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8
21245 #define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9
21246 #define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10
21247 #define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11
21248 #define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12
21249 #define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13
21250 #define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14
21251 #define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15
21252 #define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16
21253 #define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17
21254 #define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18
21255 #define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19
21256 #define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20
21257 #define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21
21258 #define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22
21259 #define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23
21260 #define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24
21261 #define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25
21262 #define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26
21263 #define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27
21264 #define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28
21265 #define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29
21266 #define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30
21267 #define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31
21268 #define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER
21269 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT
21270 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
21271 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER
21272 #define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT
21273 #define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT
21274 #define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER
21275 #define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT
21276 #define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT
21277 #define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER
21278 #define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER
21280 typedef pa_channel_map_def_t ma_pa_channel_map_def_t;
21281 #define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF
21282 #define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA
21283 #define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX
21284 #define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX
21285 #define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS
21286 #define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT
21288 typedef pa_sample_format_t ma_pa_sample_format_t;
21289 #define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID
21290 #define MA_PA_SAMPLE_U8 PA_SAMPLE_U8
21291 #define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW
21292 #define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW
21293 #define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE
21294 #define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE
21295 #define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE
21296 #define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE
21297 #define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE
21298 #define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE
21299 #define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE
21300 #define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE
21301 #define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE
21302 #define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE
21304 typedef pa_mainloop ma_pa_mainloop;
21305 typedef pa_threaded_mainloop ma_pa_threaded_mainloop;
21306 typedef pa_mainloop_api ma_pa_mainloop_api;
21307 typedef pa_context ma_pa_context;
21308 typedef pa_operation ma_pa_operation;
21309 typedef pa_stream ma_pa_stream;
21310 typedef pa_spawn_api ma_pa_spawn_api;
21311 typedef pa_buffer_attr ma_pa_buffer_attr;
21312 typedef pa_channel_map ma_pa_channel_map;
21313 typedef pa_cvolume ma_pa_cvolume;
21314 typedef pa_sample_spec ma_pa_sample_spec;
21315 typedef pa_sink_info ma_pa_sink_info;
21316 typedef pa_source_info ma_pa_source_info;
21318 typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t;
21319 typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t;
21320 typedef pa_source_info_cb_t ma_pa_source_info_cb_t;
21321 typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t;
21322 typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t;
21323 typedef pa_free_cb_t ma_pa_free_cb_t;
21326 #define MA_PA_ERR_ACCESS 1
21327 #define MA_PA_ERR_INVALID 2
21328 #define MA_PA_ERR_NOENTITY 5
21330 #define MA_PA_CHANNELS_MAX 32
21331 #define MA_PA_RATE_MAX 384000
21333 typedef int ma_pa_context_flags_t;
21334 #define MA_PA_CONTEXT_NOFLAGS 0x00000000
21335 #define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001
21336 #define MA_PA_CONTEXT_NOFAIL 0x00000002
21338 typedef int ma_pa_stream_flags_t;
21339 #define MA_PA_STREAM_NOFLAGS 0x00000000
21340 #define MA_PA_STREAM_START_CORKED 0x00000001
21341 #define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002
21342 #define MA_PA_STREAM_NOT_MONOTONIC 0x00000004
21343 #define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008
21344 #define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010
21345 #define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020
21346 #define MA_PA_STREAM_FIX_FORMAT 0x00000040
21347 #define MA_PA_STREAM_FIX_RATE 0x00000080
21348 #define MA_PA_STREAM_FIX_CHANNELS 0x00000100
21349 #define MA_PA_STREAM_DONT_MOVE 0x00000200
21350 #define MA_PA_STREAM_VARIABLE_RATE 0x00000400
21351 #define MA_PA_STREAM_PEAK_DETECT 0x00000800
21352 #define MA_PA_STREAM_START_MUTED 0x00001000
21353 #define MA_PA_STREAM_ADJUST_LATENCY 0x00002000
21354 #define MA_PA_STREAM_EARLY_REQUESTS 0x00004000
21355 #define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000
21356 #define MA_PA_STREAM_START_UNMUTED 0x00010000
21357 #define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000
21358 #define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000
21359 #define MA_PA_STREAM_PASSTHROUGH 0x00080000
21361 typedef int ma_pa_sink_flags_t;
21362 #define MA_PA_SINK_NOFLAGS 0x00000000
21363 #define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001
21364 #define MA_PA_SINK_LATENCY 0x00000002
21365 #define MA_PA_SINK_HARDWARE 0x00000004
21366 #define MA_PA_SINK_NETWORK 0x00000008
21367 #define MA_PA_SINK_HW_MUTE_CTRL 0x00000010
21368 #define MA_PA_SINK_DECIBEL_VOLUME 0x00000020
21369 #define MA_PA_SINK_FLAT_VOLUME 0x00000040
21370 #define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080
21371 #define MA_PA_SINK_SET_FORMATS 0x00000100
21373 typedef int ma_pa_source_flags_t;
21374 #define MA_PA_SOURCE_NOFLAGS 0x00000000
21375 #define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001
21376 #define MA_PA_SOURCE_LATENCY 0x00000002
21377 #define MA_PA_SOURCE_HARDWARE 0x00000004
21378 #define MA_PA_SOURCE_NETWORK 0x00000008
21379 #define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010
21380 #define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020
21381 #define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040
21382 #define MA_PA_SOURCE_FLAT_VOLUME 0x00000080
21384 typedef int ma_pa_context_state_t;
21385 #define MA_PA_CONTEXT_UNCONNECTED 0
21386 #define MA_PA_CONTEXT_CONNECTING 1
21387 #define MA_PA_CONTEXT_AUTHORIZING 2
21388 #define MA_PA_CONTEXT_SETTING_NAME 3
21389 #define MA_PA_CONTEXT_READY 4
21390 #define MA_PA_CONTEXT_FAILED 5
21391 #define MA_PA_CONTEXT_TERMINATED 6
21393 typedef int ma_pa_stream_state_t;
21394 #define MA_PA_STREAM_UNCONNECTED 0
21395 #define MA_PA_STREAM_CREATING 1
21396 #define MA_PA_STREAM_READY 2
21397 #define MA_PA_STREAM_FAILED 3
21398 #define MA_PA_STREAM_TERMINATED 4
21400 typedef int ma_pa_operation_state_t;
21401 #define MA_PA_OPERATION_RUNNING 0
21402 #define MA_PA_OPERATION_DONE 1
21403 #define MA_PA_OPERATION_CANCELLED 2
21405 typedef int ma_pa_sink_state_t;
21406 #define MA_PA_SINK_INVALID_STATE -1
21407 #define MA_PA_SINK_RUNNING 0
21408 #define MA_PA_SINK_IDLE 1
21409 #define MA_PA_SINK_SUSPENDED 2
21411 typedef int ma_pa_source_state_t;
21412 #define MA_PA_SOURCE_INVALID_STATE -1
21413 #define MA_PA_SOURCE_RUNNING 0
21414 #define MA_PA_SOURCE_IDLE 1
21415 #define MA_PA_SOURCE_SUSPENDED 2
21417 typedef int ma_pa_seek_mode_t;
21418 #define MA_PA_SEEK_RELATIVE 0
21419 #define MA_PA_SEEK_ABSOLUTE 1
21420 #define MA_PA_SEEK_RELATIVE_ON_READ 2
21421 #define MA_PA_SEEK_RELATIVE_END 3
21423 typedef int ma_pa_channel_position_t;
21424 #define MA_PA_CHANNEL_POSITION_INVALID -1
21425 #define MA_PA_CHANNEL_POSITION_MONO 0
21426 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1
21427 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2
21428 #define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3
21429 #define MA_PA_CHANNEL_POSITION_REAR_CENTER 4
21430 #define MA_PA_CHANNEL_POSITION_REAR_LEFT 5
21431 #define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6
21432 #define MA_PA_CHANNEL_POSITION_LFE 7
21433 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8
21434 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9
21435 #define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10
21436 #define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11
21437 #define MA_PA_CHANNEL_POSITION_AUX0 12
21438 #define MA_PA_CHANNEL_POSITION_AUX1 13
21439 #define MA_PA_CHANNEL_POSITION_AUX2 14
21440 #define MA_PA_CHANNEL_POSITION_AUX3 15
21441 #define MA_PA_CHANNEL_POSITION_AUX4 16
21442 #define MA_PA_CHANNEL_POSITION_AUX5 17
21443 #define MA_PA_CHANNEL_POSITION_AUX6 18
21444 #define MA_PA_CHANNEL_POSITION_AUX7 19
21445 #define MA_PA_CHANNEL_POSITION_AUX8 20
21446 #define MA_PA_CHANNEL_POSITION_AUX9 21
21447 #define MA_PA_CHANNEL_POSITION_AUX10 22
21448 #define MA_PA_CHANNEL_POSITION_AUX11 23
21449 #define MA_PA_CHANNEL_POSITION_AUX12 24
21450 #define MA_PA_CHANNEL_POSITION_AUX13 25
21451 #define MA_PA_CHANNEL_POSITION_AUX14 26
21452 #define MA_PA_CHANNEL_POSITION_AUX15 27
21453 #define MA_PA_CHANNEL_POSITION_AUX16 28
21454 #define MA_PA_CHANNEL_POSITION_AUX17 29
21455 #define MA_PA_CHANNEL_POSITION_AUX18 30
21456 #define MA_PA_CHANNEL_POSITION_AUX19 31
21457 #define MA_PA_CHANNEL_POSITION_AUX20 32
21458 #define MA_PA_CHANNEL_POSITION_AUX21 33
21459 #define MA_PA_CHANNEL_POSITION_AUX22 34
21460 #define MA_PA_CHANNEL_POSITION_AUX23 35
21461 #define MA_PA_CHANNEL_POSITION_AUX24 36
21462 #define MA_PA_CHANNEL_POSITION_AUX25 37
21463 #define MA_PA_CHANNEL_POSITION_AUX26 38
21464 #define MA_PA_CHANNEL_POSITION_AUX27 39
21465 #define MA_PA_CHANNEL_POSITION_AUX28 40
21466 #define MA_PA_CHANNEL_POSITION_AUX29 41
21467 #define MA_PA_CHANNEL_POSITION_AUX30 42
21468 #define MA_PA_CHANNEL_POSITION_AUX31 43
21469 #define MA_PA_CHANNEL_POSITION_TOP_CENTER 44
21470 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45
21471 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46
21472 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47
21473 #define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48
21474 #define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49
21475 #define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50
21476 #define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT
21477 #define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT
21478 #define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER
21479 #define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE
21481 typedef int ma_pa_channel_map_def_t;
21482 #define MA_PA_CHANNEL_MAP_AIFF 0
21483 #define MA_PA_CHANNEL_MAP_ALSA 1
21484 #define MA_PA_CHANNEL_MAP_AUX 2
21485 #define MA_PA_CHANNEL_MAP_WAVEEX 3
21486 #define MA_PA_CHANNEL_MAP_OSS 4
21487 #define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF
21489 typedef int ma_pa_sample_format_t;
21490 #define MA_PA_SAMPLE_INVALID -1
21491 #define MA_PA_SAMPLE_U8 0
21492 #define MA_PA_SAMPLE_ALAW 1
21493 #define MA_PA_SAMPLE_ULAW 2
21494 #define MA_PA_SAMPLE_S16LE 3
21495 #define MA_PA_SAMPLE_S16BE 4
21496 #define MA_PA_SAMPLE_FLOAT32LE 5
21497 #define MA_PA_SAMPLE_FLOAT32BE 6
21498 #define MA_PA_SAMPLE_S32LE 7
21499 #define MA_PA_SAMPLE_S32BE 8
21500 #define MA_PA_SAMPLE_S24LE 9
21501 #define MA_PA_SAMPLE_S24BE 10
21502 #define MA_PA_SAMPLE_S24_32LE 11
21503 #define MA_PA_SAMPLE_S24_32BE 12
21505 typedef struct ma_pa_mainloop ma_pa_mainloop;
21506 typedef struct ma_pa_threaded_mainloop ma_pa_threaded_mainloop;
21507 typedef struct ma_pa_mainloop_api ma_pa_mainloop_api;
21508 typedef struct ma_pa_context ma_pa_context;
21509 typedef struct ma_pa_operation ma_pa_operation;
21510 typedef struct ma_pa_stream ma_pa_stream;
21511 typedef struct ma_pa_spawn_api ma_pa_spawn_api;
21515 ma_uint32 maxlength;
21519 ma_uint32 fragsize;
21520 } ma_pa_buffer_attr;
21525 ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX];
21526 } ma_pa_channel_map;
21531 ma_uint32 values[MA_PA_CHANNELS_MAX];
21536 ma_pa_sample_format_t format;
21539 } ma_pa_sample_spec;
21545 const char* description;
21546 ma_pa_sample_spec sample_spec;
21547 ma_pa_channel_map channel_map;
21548 ma_uint32 owner_module;
21549 ma_pa_cvolume volume;
21551 ma_uint32 monitor_source;
21552 const char* monitor_source_name;
21554 const char* driver;
21555 ma_pa_sink_flags_t flags;
21557 ma_uint64 configured_latency;
21558 ma_uint32 base_volume;
21559 ma_pa_sink_state_t state;
21560 ma_uint32 n_volume_steps;
21565 ma_uint8 n_formats;
21573 const char *description;
21574 ma_pa_sample_spec sample_spec;
21575 ma_pa_channel_map channel_map;
21576 ma_uint32 owner_module;
21577 ma_pa_cvolume volume;
21579 ma_uint32 monitor_of_sink;
21580 const char *monitor_of_sink_name;
21582 const char *driver;
21583 ma_pa_source_flags_t flags;
21585 ma_uint64 configured_latency;
21586 ma_uint32 base_volume;
21587 ma_pa_source_state_t state;
21588 ma_uint32 n_volume_steps;
21593 ma_uint8 n_formats;
21595 } ma_pa_source_info;
21597 typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata);
21598 typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata);
21599 typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata);
21600 typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata);
21601 typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata);
21602 typedef void (* ma_pa_free_cb_t) (void* p);
21606 typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void);
21607 typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m);
21608 typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval);
21609 typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m);
21610 typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval);
21611 typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m);
21612 typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void);
21613 typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m);
21614 typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m);
21615 typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m);
21616 typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m);
21617 typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m);
21618 typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m);
21619 typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept);
21620 typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m);
21621 typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m);
21622 typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m);
21623 typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m);
21624 typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name);
21625 typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name);
21626 typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c);
21627 typedef int (* ma_pa_context_connect_proc) (ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api);
21628 typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c);
21629 typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata);
21630 typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c);
21631 typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata);
21632 typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata);
21633 typedef ma_pa_operation* (* ma_pa_context_get_sink_info_by_name_proc) (ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata);
21634 typedef ma_pa_operation* (* ma_pa_context_get_source_info_by_name_proc)(ma_pa_context* c, const char* name, ma_pa_source_info_cb_t cb, void* userdata);
21635 typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o);
21636 typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o);
21637 typedef ma_pa_channel_map* (* ma_pa_channel_map_init_extend_proc) (ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def);
21638 typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m);
21639 typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss);
21640 typedef ma_pa_stream* (* ma_pa_stream_new_proc) (ma_pa_context* c, const char* name, const ma_pa_sample_spec* ss, const ma_pa_channel_map* map);
21641 typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s);
21642 typedef int (* ma_pa_stream_connect_playback_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream);
21643 typedef int (* ma_pa_stream_connect_record_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags);
21644 typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s);
21645 typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s);
21646 typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s);
21647 typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s);
21648 typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s);
21649 typedef ma_pa_operation* (* ma_pa_stream_set_buffer_attr_proc) (ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata);
21650 typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s);
21651 typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
21652 typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
21653 typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
21654 typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
21655 typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s);
21656 typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata);
21657 typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
21658 typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes);
21659 typedef int (* ma_pa_stream_write_proc) (ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek);
21660 typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes);
21661 typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s);
21662 typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s);
21663 typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s);
21668 ma_uint32 capacity;
21669 ma_device_info* pInfo;
21670 } ma_pulse_device_enum_data;
21672 static ma_result ma_result_from_pulse(int result)
21679 case MA_PA_OK: return MA_SUCCESS;
21680 case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED;
21681 case MA_PA_ERR_INVALID: return MA_INVALID_ARGS;
21682 case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE;
21683 default: return MA_ERROR;
21688 static ma_pa_sample_format_t ma_format_to_pulse(ma_format format)
21690 if (ma_is_little_endian()) {
21692 case ma_format_s16: return MA_PA_SAMPLE_S16LE;
21693 case ma_format_s24: return MA_PA_SAMPLE_S24LE;
21694 case ma_format_s32: return MA_PA_SAMPLE_S32LE;
21695 case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE;
21700 case ma_format_s16: return MA_PA_SAMPLE_S16BE;
21701 case ma_format_s24: return MA_PA_SAMPLE_S24BE;
21702 case ma_format_s32: return MA_PA_SAMPLE_S32BE;
21703 case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE;
21708 /* Endian agnostic. */
21710 case ma_format_u8: return MA_PA_SAMPLE_U8;
21711 default: return MA_PA_SAMPLE_INVALID;
21716 static ma_format ma_format_from_pulse(ma_pa_sample_format_t format)
21718 if (ma_is_little_endian()) {
21720 case MA_PA_SAMPLE_S16LE: return ma_format_s16;
21721 case MA_PA_SAMPLE_S24LE: return ma_format_s24;
21722 case MA_PA_SAMPLE_S32LE: return ma_format_s32;
21723 case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32;
21728 case MA_PA_SAMPLE_S16BE: return ma_format_s16;
21729 case MA_PA_SAMPLE_S24BE: return ma_format_s24;
21730 case MA_PA_SAMPLE_S32BE: return ma_format_s32;
21731 case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32;
21736 /* Endian agnostic. */
21738 case MA_PA_SAMPLE_U8: return ma_format_u8;
21739 default: return ma_format_unknown;
21743 static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position)
21747 case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE;
21748 case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO;
21749 case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
21750 case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
21751 case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
21752 case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER;
21753 case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT;
21754 case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT;
21755 case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE;
21756 case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
21757 case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
21758 case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
21759 case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
21760 case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0;
21761 case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1;
21762 case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2;
21763 case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3;
21764 case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4;
21765 case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5;
21766 case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6;
21767 case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7;
21768 case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8;
21769 case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9;
21770 case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10;
21771 case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11;
21772 case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12;
21773 case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13;
21774 case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14;
21775 case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15;
21776 case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16;
21777 case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17;
21778 case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18;
21779 case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19;
21780 case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20;
21781 case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21;
21782 case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22;
21783 case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23;
21784 case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24;
21785 case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25;
21786 case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26;
21787 case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27;
21788 case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28;
21789 case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29;
21790 case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30;
21791 case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31;
21792 case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
21793 case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
21794 case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
21795 case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
21796 case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
21797 case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
21798 case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
21799 default: return MA_CHANNEL_NONE;
21804 static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position)
21808 case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID;
21809 case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT;
21810 case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT;
21811 case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER;
21812 case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE;
21813 case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT;
21814 case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT;
21815 case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
21816 case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
21817 case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER;
21818 case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT;
21819 case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT;
21820 case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER;
21821 case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
21822 case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
21823 case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
21824 case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT;
21825 case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER;
21826 case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
21827 case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18;
21828 case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19;
21829 case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20;
21830 case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21;
21831 case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22;
21832 case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23;
21833 case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24;
21834 case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25;
21835 case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26;
21836 case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27;
21837 case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28;
21838 case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29;
21839 case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30;
21840 case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31;
21841 default: return (ma_pa_channel_position_t)position;
21846 static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_operation* pOP)
21849 ma_pa_operation_state_t state;
21851 MA_ASSERT(pContext != NULL);
21852 MA_ASSERT(pOP != NULL);
21855 state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP);
21856 if (state != MA_PA_OPERATION_RUNNING) {
21860 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pContext->pulse.pMainLoop, 1, NULL);
21861 if (resultPA < 0) {
21862 return ma_result_from_pulse(resultPA);
21869 static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_pa_operation* pOP)
21874 return MA_INVALID_ARGS;
21877 result = ma_wait_for_operation__pulse(pContext, pOP);
21878 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
21883 static ma_result ma_context_wait_for_pa_context_to_connect__pulse(ma_context* pContext)
21886 ma_pa_context_state_t state;
21889 state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pContext->pulse.pPulseContext);
21890 if (state == MA_PA_CONTEXT_READY) {
21894 if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {
21895 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context.", MA_ERROR);
21898 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pContext->pulse.pMainLoop, 1, NULL);
21899 if (resultPA < 0) {
21900 return ma_result_from_pulse(resultPA);
21904 /* Should never get here. */
21908 static ma_result ma_context_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_pa_stream* pStream)
21911 ma_pa_stream_state_t state;
21914 state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)(pStream);
21915 if (state == MA_PA_STREAM_READY) {
21919 if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) {
21920 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream.", MA_ERROR);
21923 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pContext->pulse.pMainLoop, 1, NULL);
21924 if (resultPA < 0) {
21925 return ma_result_from_pulse(resultPA);
21933 static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
21935 ma_pa_sink_info* pInfoOut;
21937 if (endOfList > 0) {
21941 pInfoOut = (ma_pa_sink_info*)pUserData;
21942 MA_ASSERT(pInfoOut != NULL);
21944 *pInfoOut = *pInfo;
21946 (void)pPulseContext; /* Unused. */
21949 static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
21951 ma_pa_source_info* pInfoOut;
21953 if (endOfList > 0) {
21957 pInfoOut = (ma_pa_source_info*)pUserData;
21958 MA_ASSERT(pInfoOut != NULL);
21960 *pInfoOut = *pInfo;
21962 (void)pPulseContext; /* Unused. */
21965 static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
21967 ma_device* pDevice;
21969 if (endOfList > 0) {
21973 pDevice = (ma_device*)pUserData;
21974 MA_ASSERT(pDevice != NULL);
21976 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1);
21978 (void)pPulseContext; /* Unused. */
21981 static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
21983 ma_device* pDevice;
21985 if (endOfList > 0) {
21989 pDevice = (ma_device*)pUserData;
21990 MA_ASSERT(pDevice != NULL);
21992 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1);
21994 (void)pPulseContext; /* Unused. */
21998 static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo)
22000 ma_pa_operation* pOP;
22002 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);
22007 return ma_wait_for_operation_and_unref__pulse(pContext, pOP);
22010 static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo)
22012 ma_pa_operation* pOP;
22014 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);
22019 return ma_wait_for_operation_and_unref__pulse(pContext, pOP);;
22022 static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex)
22026 MA_ASSERT(pContext != NULL);
22027 MA_ASSERT(pIndex != NULL);
22029 if (pIndex != NULL) {
22030 *pIndex = (ma_uint32)-1;
22033 if (deviceType == ma_device_type_playback) {
22034 ma_pa_sink_info sinkInfo;
22035 result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo);
22036 if (result != MA_SUCCESS) {
22040 if (pIndex != NULL) {
22041 *pIndex = sinkInfo.index;
22045 if (deviceType == ma_device_type_capture) {
22046 ma_pa_source_info sourceInfo;
22047 result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo);
22048 if (result != MA_SUCCESS) {
22052 if (pIndex != NULL) {
22053 *pIndex = sourceInfo.index;
22063 ma_context* pContext;
22064 ma_enum_devices_callback_proc callback;
22066 ma_bool32 isTerminated;
22067 ma_uint32 defaultDeviceIndexPlayback;
22068 ma_uint32 defaultDeviceIndexCapture;
22069 } ma_context_enumerate_devices_callback_data__pulse;
22071 static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData)
22073 ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
22074 ma_device_info deviceInfo;
22076 MA_ASSERT(pData != NULL);
22078 if (endOfList || pData->isTerminated) {
22082 MA_ZERO_OBJECT(&deviceInfo);
22084 /* The name from PulseAudio is the ID for miniaudio. */
22085 if (pSinkInfo->name != NULL) {
22086 ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1);
22089 /* The description from PulseAudio is the name for miniaudio. */
22090 if (pSinkInfo->description != NULL) {
22091 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1);
22094 if (pSinkInfo->index == pData->defaultDeviceIndexPlayback) {
22095 deviceInfo.isDefault = MA_TRUE;
22098 pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData);
22100 (void)pPulseContext; /* Unused. */
22103 static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData)
22105 ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
22106 ma_device_info deviceInfo;
22108 MA_ASSERT(pData != NULL);
22110 if (endOfList || pData->isTerminated) {
22114 MA_ZERO_OBJECT(&deviceInfo);
22116 /* The name from PulseAudio is the ID for miniaudio. */
22117 if (pSourceInfo->name != NULL) {
22118 ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSourceInfo->name, (size_t)-1);
22121 /* The description from PulseAudio is the name for miniaudio. */
22122 if (pSourceInfo->description != NULL) {
22123 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSourceInfo->description, (size_t)-1);
22126 if (pSourceInfo->index == pData->defaultDeviceIndexCapture) {
22127 deviceInfo.isDefault = MA_TRUE;
22130 pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData);
22132 (void)pPulseContext; /* Unused. */
22135 static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
22137 ma_result result = MA_SUCCESS;
22138 ma_context_enumerate_devices_callback_data__pulse callbackData;
22139 ma_pa_operation* pOP = NULL;
22141 MA_ASSERT(pContext != NULL);
22142 MA_ASSERT(callback != NULL);
22144 callbackData.pContext = pContext;
22145 callbackData.callback = callback;
22146 callbackData.pUserData = pUserData;
22147 callbackData.isTerminated = MA_FALSE;
22148 callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1;
22149 callbackData.defaultDeviceIndexCapture = (ma_uint32)-1;
22151 /* We need to get the index of the default devices. */
22152 ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback);
22153 ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture);
22156 if (!callbackData.isTerminated) {
22157 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);
22163 result = ma_wait_for_operation__pulse(pContext, pOP);
22164 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
22166 if (result != MA_SUCCESS) {
22173 if (!callbackData.isTerminated) {
22174 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);
22180 result = ma_wait_for_operation__pulse(pContext, pOP);
22181 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
22183 if (result != MA_SUCCESS) {
22195 ma_device_info* pDeviceInfo;
22196 ma_uint32 defaultDeviceIndex;
22197 ma_bool32 foundDevice;
22198 } ma_context_get_device_info_callback_data__pulse;
22200 static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
22202 ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
22204 if (endOfList > 0) {
22208 MA_ASSERT(pData != NULL);
22209 pData->foundDevice = MA_TRUE;
22211 if (pInfo->name != NULL) {
22212 ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
22215 if (pInfo->description != NULL) {
22216 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
22220 We're just reporting a single data format here. I think technically PulseAudio might support
22221 all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to
22222 report the "native" device format.
22224 pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format);
22225 pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels;
22226 pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate;
22227 pData->pDeviceInfo->nativeDataFormats[0].flags = 0;
22228 pData->pDeviceInfo->nativeDataFormatCount = 1;
22230 if (pData->defaultDeviceIndex == pInfo->index) {
22231 pData->pDeviceInfo->isDefault = MA_TRUE;
22234 (void)pPulseContext; /* Unused. */
22237 static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
22239 ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
22241 if (endOfList > 0) {
22245 MA_ASSERT(pData != NULL);
22246 pData->foundDevice = MA_TRUE;
22248 if (pInfo->name != NULL) {
22249 ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
22252 if (pInfo->description != NULL) {
22253 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
22257 We're just reporting a single data format here. I think technically PulseAudio might support
22258 all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to
22259 report the "native" device format.
22261 pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format);
22262 pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels;
22263 pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate;
22264 pData->pDeviceInfo->nativeDataFormats[0].flags = 0;
22265 pData->pDeviceInfo->nativeDataFormatCount = 1;
22267 if (pData->defaultDeviceIndex == pInfo->index) {
22268 pData->pDeviceInfo->isDefault = MA_TRUE;
22271 (void)pPulseContext; /* Unused. */
22274 static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
22276 ma_result result = MA_SUCCESS;
22277 ma_context_get_device_info_callback_data__pulse callbackData;
22278 ma_pa_operation* pOP = NULL;
22280 MA_ASSERT(pContext != NULL);
22282 callbackData.pDeviceInfo = pDeviceInfo;
22283 callbackData.foundDevice = MA_FALSE;
22285 result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex);
22287 if (deviceType == ma_device_type_playback) {
22288 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), pDeviceID->pulse, ma_context_get_device_info_sink_callback__pulse, &callbackData);
22290 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), pDeviceID->pulse, ma_context_get_device_info_source_callback__pulse, &callbackData);
22294 ma_wait_for_operation_and_unref__pulse(pContext, pOP);
22300 if (!callbackData.foundDevice) {
22301 result = MA_NO_DEVICE;
22309 static ma_result ma_device_uninit__pulse(ma_device* pDevice)
22311 ma_context* pContext;
22313 MA_ASSERT(pDevice != NULL);
22315 pContext = pDevice->pContext;
22316 MA_ASSERT(pContext != NULL);
22318 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
22319 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
22320 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
22323 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
22324 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
22325 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
22328 if (pDevice->type == ma_device_type_duplex) {
22329 ma_duplex_rb_uninit(&pDevice->duplexRB);
22335 static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss)
22337 ma_pa_buffer_attr attr;
22338 attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels);
22339 attr.tlength = attr.maxlength / periods;
22340 attr.prebuf = (ma_uint32)-1;
22341 attr.minreq = (ma_uint32)-1;
22342 attr.fragsize = attr.maxlength / periods;
22347 static 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)
22349 static int g_StreamCounter = 0;
22350 char actualStreamName[256];
22352 if (pStreamName != NULL) {
22353 ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1);
22355 ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:");
22356 ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */
22358 g_StreamCounter += 1;
22360 return ((ma_pa_stream_new_proc)pContext->pulse.pa_stream_new)((ma_pa_context*)pContext->pulse.pPulseContext, actualStreamName, ss, cmap);
22364 static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData)
22366 ma_device* pDevice = (ma_device*)pUserData;
22368 ma_uint64 frameCount;
22369 ma_uint64 framesProcessed;
22371 MA_ASSERT(pDevice != NULL);
22373 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
22374 MA_ASSERT(bpf > 0);
22376 frameCount = byteCount / bpf;
22377 framesProcessed = 0;
22379 while (ma_device_get_state(pDevice) == MA_STATE_STARTED && framesProcessed < frameCount) {
22380 const void* pMappedPCMFrames;
22381 size_t bytesMapped;
22382 ma_uint64 framesMapped;
22384 int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped);
22385 if (pulseResult < 0) {
22386 break; /* Failed to map. Abort. */
22389 framesMapped = bytesMapped / bpf;
22390 if (framesMapped > 0) {
22391 if (pMappedPCMFrames != NULL) {
22392 ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped);
22395 #if defined(MA_DEBUG_OUTPUT)
22396 printf("[PulseAudio] ma_device_on_read__pulse: Hole.\n");
22400 pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream);
22401 if (pulseResult < 0) {
22402 break; /* Failed to drop the buffer. */
22405 framesProcessed += framesMapped;
22408 /* Nothing was mapped. Just abort. */
22414 static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed)
22416 ma_result result = MA_SUCCESS;
22417 ma_uint64 framesProcessed = 0;
22418 size_t bytesMapped;
22420 ma_uint32 deviceState;
22422 MA_ASSERT(pDevice != NULL);
22423 MA_ASSERT(pStream != NULL);
22425 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
22426 MA_ASSERT(bpf > 0);
22428 deviceState = ma_device_get_state(pDevice);
22430 bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream);
22431 if (bytesMapped != (size_t)-1) {
22432 if (bytesMapped > 0) {
22433 ma_uint64 framesMapped;
22434 void* pMappedPCMFrames;
22435 int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped);
22436 if (pulseResult < 0) {
22437 result = ma_result_from_pulse(pulseResult);
22441 framesMapped = bytesMapped / bpf;
22443 if (deviceState == MA_STATE_STARTED) {
22444 ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped);
22446 /* Device is not started. Don't write anything to it. */
22449 pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE);
22450 if (pulseResult < 0) {
22451 result = ma_result_from_pulse(pulseResult);
22452 goto done; /* Failed to write data to stream. */
22455 framesProcessed += framesMapped;
22457 result = MA_ERROR; /* No data available. Abort. */
22461 result = MA_ERROR; /* Failed to retrieve the writable size. Abort. */
22466 if (pFramesProcessed != NULL) {
22467 *pFramesProcessed = framesProcessed;
22473 static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData)
22475 ma_device* pDevice = (ma_device*)pUserData;
22477 ma_uint64 frameCount;
22478 ma_uint64 framesProcessed;
22479 ma_uint32 deviceState;
22482 MA_ASSERT(pDevice != NULL);
22485 Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio
22486 can fire this callback before the stream has even started. Ridiculous.
22488 deviceState = ma_device_get_state(pDevice);
22489 if (deviceState != MA_STATE_STARTING && deviceState != MA_STATE_STARTED) {
22493 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
22494 MA_ASSERT(bpf > 0);
22496 frameCount = byteCount / bpf;
22497 framesProcessed = 0;
22499 while (framesProcessed < frameCount) {
22500 ma_uint64 framesProcessedThisIteration;
22502 /* Don't keep trying to process frames if the device isn't started. */
22503 deviceState = ma_device_get_state(pDevice);
22504 if (deviceState != MA_STATE_STARTING && deviceState != MA_STATE_STARTED) {
22508 result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration);
22509 if (result != MA_SUCCESS) {
22513 framesProcessed += framesProcessedThisIteration;
22517 static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
22520 Notes for PulseAudio:
22522 - We're always using native format/channels/rate regardless of whether or not PulseAudio
22523 supports the format directly through their own data conversion system. I'm doing this to
22524 reduce as much variability from the PulseAudio side as possible because it's seems to be
22525 extremely unreliable at everything it does.
22527 - When both the period size in frames and milliseconds are 0, we default to miniaudio's
22528 default buffer sizes rather than leaving it up to PulseAudio because I don't trust
22529 PulseAudio to give us any kind of reasonable latency by default.
22531 - Do not ever, *ever* forget to use MA_PA_STREAM_ADJUST_LATENCY. If you don't specify this
22532 flag, capture mode will just not work properly until you open another PulseAudio app.
22535 ma_result result = MA_SUCCESS;
22537 const char* devPlayback = NULL;
22538 const char* devCapture = NULL;
22539 ma_format format = ma_format_unknown;
22540 ma_uint32 channels = 0;
22541 ma_uint32 sampleRate = 0;
22542 ma_pa_sink_info sinkInfo;
22543 ma_pa_source_info sourceInfo;
22544 ma_pa_sample_spec ss;
22545 ma_pa_channel_map cmap;
22546 ma_pa_buffer_attr attr;
22547 const ma_pa_sample_spec* pActualSS = NULL;
22548 const ma_pa_channel_map* pActualCMap = NULL;
22549 const ma_pa_buffer_attr* pActualAttr = NULL;
22550 ma_uint32 iChannel;
22551 ma_pa_stream_flags_t streamFlags;
22553 MA_ASSERT(pDevice != NULL);
22554 MA_ZERO_OBJECT(&pDevice->pulse);
22556 if (pConfig->deviceType == ma_device_type_loopback) {
22557 return MA_DEVICE_TYPE_NOT_SUPPORTED;
22560 /* No exclusive mode with the PulseAudio backend. */
22561 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
22562 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
22563 return MA_SHARE_MODE_NOT_SUPPORTED;
22566 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
22567 if (pDescriptorPlayback->pDeviceID != NULL) {
22568 devPlayback = pDescriptorPlayback->pDeviceID->pulse;
22571 format = pDescriptorPlayback->format;
22572 channels = pDescriptorPlayback->channels;
22573 sampleRate = pDescriptorPlayback->sampleRate;
22576 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
22577 if (pDescriptorCapture->pDeviceID != NULL) {
22578 devCapture = pDescriptorCapture->pDeviceID->pulse;
22581 format = pDescriptorCapture->format;
22582 channels = pDescriptorCapture->channels;
22583 sampleRate = pDescriptorCapture->sampleRate;
22586 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
22587 result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo);
22588 if (result != MA_SUCCESS) {
22589 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device.", result);
22593 ss = sourceInfo.sample_spec;
22594 cmap = sourceInfo.channel_map;
22596 /* We now have enough information to calculate our actual period size in frames. */
22597 pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, ss.rate, pConfig->performanceProfile);
22599 attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss);
22600 #ifdef MA_DEBUG_OUTPUT
22601 printf("[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);
22604 pDevice->pulse.pStreamCapture = ma_context__pa_stream_new__pulse(pDevice->pContext, pConfig->pulse.pStreamNameCapture, &ss, &cmap);
22605 if (pDevice->pulse.pStreamCapture == NULL) {
22606 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22611 /* The callback needs to be set before connecting the stream. */
22612 ((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);
22615 /* Connect after we've got all of our internal state set up. */
22616 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;
22617 if (devCapture != NULL) {
22618 streamFlags |= MA_PA_STREAM_DONT_MOVE;
22621 error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags);
22622 if (error != MA_PA_OK) {
22623 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream.", ma_result_from_pulse(error));
22627 result = ma_context_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, (ma_pa_stream*)pDevice->pulse.pStreamCapture);
22628 if (result != MA_SUCCESS) {
22632 /* Internal format. */
22633 pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
22634 if (pActualSS != NULL) {
22638 pDescriptorCapture->format = ma_format_from_pulse(ss.format);
22639 pDescriptorCapture->channels = ss.channels;
22640 pDescriptorCapture->sampleRate = ss.rate;
22642 /* Internal channel map. */
22643 pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
22644 if (pActualCMap != NULL) {
22645 cmap = *pActualCMap;
22648 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
22649 pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
22654 pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
22655 if (pActualAttr != NULL) {
22656 attr = *pActualAttr;
22659 pDescriptorCapture->periodCount = attr.maxlength / attr.fragsize;
22660 pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount;
22661 #ifdef MA_DEBUG_OUTPUT
22662 printf("[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);
22667 devCapture = ((ma_pa_stream_get_device_name_proc)pDevice->pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
22668 if (devCapture != NULL) {
22669 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);
22670 ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP);
22674 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
22675 result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo);
22676 if (result != MA_SUCCESS) {
22677 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.", result);
22681 ss = sinkInfo.sample_spec;
22682 cmap = sinkInfo.channel_map;
22684 /* We now have enough information to calculate the actual buffer size in frames. */
22685 pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, ss.rate, pConfig->performanceProfile);
22687 attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss);
22688 #ifdef MA_DEBUG_OUTPUT
22689 printf("[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);
22692 pDevice->pulse.pStreamPlayback = ma_context__pa_stream_new__pulse(pDevice->pContext, pConfig->pulse.pStreamNamePlayback, &ss, &cmap);
22693 if (pDevice->pulse.pStreamPlayback == NULL) {
22694 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22700 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
22701 device state of MA_STATE_UNINITIALIZED.
22703 ((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);
22706 /* Connect after we've got all of our internal state set up. */
22707 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;
22708 if (devPlayback != NULL) {
22709 streamFlags |= MA_PA_STREAM_DONT_MOVE;
22712 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);
22713 if (error != MA_PA_OK) {
22714 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream.", ma_result_from_pulse(error));
22718 result = ma_context_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, (ma_pa_stream*)pDevice->pulse.pStreamPlayback);
22719 if (result != MA_SUCCESS) {
22724 /* Internal format. */
22725 pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
22726 if (pActualSS != NULL) {
22730 pDescriptorPlayback->format = ma_format_from_pulse(ss.format);
22731 pDescriptorPlayback->channels = ss.channels;
22732 pDescriptorPlayback->sampleRate = ss.rate;
22734 /* Internal channel map. */
22735 pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
22736 if (pActualCMap != NULL) {
22737 cmap = *pActualCMap;
22740 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
22741 pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
22746 pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
22747 if (pActualAttr != NULL) {
22748 attr = *pActualAttr;
22751 pDescriptorPlayback->periodCount = attr.maxlength / attr.tlength;
22752 pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount;
22753 #ifdef MA_DEBUG_OUTPUT
22754 printf("[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, pDevice->playback.internalPeriodSizeInFrames);
22759 devPlayback = ((ma_pa_stream_get_device_name_proc)pDevice->pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
22760 if (devPlayback != NULL) {
22761 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);
22762 ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP);
22768 We need a ring buffer for handling duplex mode. We can use the main duplex ring buffer in the main
22769 part of the ma_device struct. We cannot, however, depend on ma_device_init() initializing this for
22770 us later on because that will only do it if it's a fully asynchronous backend - i.e. the
22771 onDeviceDataLoop callback is NULL, which is not the case for PulseAudio.
22773 if (pConfig->deviceType == ma_device_type_duplex) {
22774 result = ma_duplex_rb_init(format, channels, sampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);
22775 if (result != MA_SUCCESS) {
22776 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer.", result);
22785 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
22786 ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
22789 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
22790 ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
22793 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
22794 ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
22797 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
22798 ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
22805 static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData)
22807 ma_bool32* pIsSuccessful = (ma_bool32*)pUserData;
22808 MA_ASSERT(pIsSuccessful != NULL);
22810 *pIsSuccessful = (ma_bool32)success;
22812 (void)pStream; /* Unused. */
22815 static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork)
22817 ma_context* pContext = pDevice->pContext;
22818 ma_bool32 wasSuccessful;
22819 ma_pa_stream* pStream;
22820 ma_pa_operation* pOP;
22823 /* This should not be called with a duplex device type. */
22824 if (deviceType == ma_device_type_duplex) {
22825 return MA_INVALID_ARGS;
22828 wasSuccessful = MA_FALSE;
22830 pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback);
22831 MA_ASSERT(pStream != NULL);
22833 pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful);
22835 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);
22838 result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP);
22839 if (result != MA_SUCCESS) {
22840 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork.", result);
22843 if (!wasSuccessful) {
22845 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to stop PulseAudio stream.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
22847 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to start PulseAudio stream.", MA_FAILED_TO_START_BACKEND_DEVICE);
22854 static ma_result ma_device_start__pulse(ma_device* pDevice)
22858 MA_ASSERT(pDevice != NULL);
22860 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
22861 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0);
22862 if (result != MA_SUCCESS) {
22867 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
22868 /* We need to fill some data before uncorking. Not doing this will result in the write callback never getting fired. */
22869 result = ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL);
22870 if (result != MA_SUCCESS) {
22871 return result; /* Failed to write data. Not sure what to do here... Just aborting. */
22874 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0);
22875 if (result != MA_SUCCESS) {
22883 static ma_result ma_device_stop__pulse(ma_device* pDevice)
22886 ma_bool32 wasSuccessful;
22888 MA_ASSERT(pDevice != NULL);
22890 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
22891 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1);
22892 if (result != MA_SUCCESS) {
22897 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
22898 /* The stream needs to be drained if it's a playback device. */
22899 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);
22900 ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP);
22902 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1);
22903 if (result != MA_SUCCESS) {
22911 static ma_result ma_device_data_loop__pulse(ma_device* pDevice)
22915 MA_ASSERT(pDevice != NULL);
22917 /* NOTE: Don't start the device here. It'll be done at a higher level. */
22920 All data is handled through callbacks. All we need to do is iterate over the main loop and let
22921 the callbacks deal with it.
22923 while (ma_device_get_state(pDevice) == MA_STATE_STARTED) {
22924 resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pContext->pulse.pMainLoop, 1, NULL);
22925 if (resultPA < 0) {
22930 /* NOTE: Don't stop the device here. It'll be done at a higher level. */
22934 static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice)
22936 MA_ASSERT(pDevice != NULL);
22938 ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pContext->pulse.pMainLoop);
22943 static ma_result ma_context_uninit__pulse(ma_context* pContext)
22945 MA_ASSERT(pContext != NULL);
22946 MA_ASSERT(pContext->backend == ma_backend_pulseaudio);
22948 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext);
22949 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext);
22950 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop);
22952 #ifndef MA_NO_RUNTIME_LINKING
22953 ma_dlclose(pContext, pContext->pulse.pulseSO);
22959 static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
22962 #ifndef MA_NO_RUNTIME_LINKING
22963 const char* libpulseNames[] = {
22969 for (i = 0; i < ma_countof(libpulseNames); ++i) {
22970 pContext->pulse.pulseSO = ma_dlopen(pContext, libpulseNames[i]);
22971 if (pContext->pulse.pulseSO != NULL) {
22976 if (pContext->pulse.pulseSO == NULL) {
22977 return MA_NO_BACKEND;
22980 pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_new");
22981 pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_free");
22982 pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_quit");
22983 pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_get_api");
22984 pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_iterate");
22985 pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_wakeup");
22986 pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_new");
22987 pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_free");
22988 pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_start");
22989 pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_stop");
22990 pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_lock");
22991 pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock");
22992 pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_wait");
22993 pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_signal");
22994 pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_accept");
22995 pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval");
22996 pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api");
22997 pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread");
22998 pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name");
22999 pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_new");
23000 pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_unref");
23001 pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_connect");
23002 pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_disconnect");
23003 pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_set_state_callback");
23004 pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_state");
23005 pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_list");
23006 pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_list");
23007 pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name");
23008 pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_by_name");
23009 pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_unref");
23010 pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_get_state");
23011 pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_init_extend");
23012 pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_valid");
23013 pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_compatible");
23014 pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_new");
23015 pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_unref");
23016 pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_playback");
23017 pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_record");
23018 pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_disconnect");
23019 pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_state");
23020 pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_sample_spec");
23021 pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_channel_map");
23022 pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_buffer_attr");
23023 pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_buffer_attr");
23024 pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_device_name");
23025 pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback");
23026 pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback");
23027 pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush");
23028 pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain");
23029 pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_corked");
23030 pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_cork");
23031 pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_trigger");
23032 pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_begin_write");
23033 pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_write");
23034 pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_peek");
23035 pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drop");
23036 pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_writable_size");
23037 pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_readable_size");
23039 /* This strange assignment system is just for type safety. */
23040 ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new;
23041 ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free;
23042 ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit;
23043 ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api;
23044 ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate;
23045 ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup;
23046 ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new;
23047 ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free;
23048 ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start;
23049 ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop;
23050 ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock;
23051 ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock;
23052 ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait;
23053 ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal;
23054 ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept;
23055 ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval;
23056 ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api;
23057 ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread;
23058 ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name;
23059 ma_pa_context_new_proc _pa_context_new = pa_context_new;
23060 ma_pa_context_unref_proc _pa_context_unref = pa_context_unref;
23061 ma_pa_context_connect_proc _pa_context_connect = pa_context_connect;
23062 ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect;
23063 ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback;
23064 ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state;
23065 ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list;
23066 ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list;
23067 ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name;
23068 ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name;
23069 ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref;
23070 ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state;
23071 ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend;
23072 ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid;
23073 ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible;
23074 ma_pa_stream_new_proc _pa_stream_new = pa_stream_new;
23075 ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref;
23076 ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback;
23077 ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record;
23078 ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect;
23079 ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state;
23080 ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec;
23081 ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map;
23082 ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr;
23083 ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr;
23084 ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name;
23085 ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback;
23086 ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback;
23087 ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush;
23088 ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain;
23089 ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked;
23090 ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork;
23091 ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger;
23092 ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write;
23093 ma_pa_stream_write_proc _pa_stream_write = pa_stream_write;
23094 ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek;
23095 ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop;
23096 ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size;
23097 ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size;
23099 pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new;
23100 pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free;
23101 pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit;
23102 pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api;
23103 pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate;
23104 pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup;
23105 pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new;
23106 pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free;
23107 pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start;
23108 pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop;
23109 pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock;
23110 pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock;
23111 pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait;
23112 pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal;
23113 pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept;
23114 pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval;
23115 pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api;
23116 pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread;
23117 pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name;
23118 pContext->pulse.pa_context_new = (ma_proc)_pa_context_new;
23119 pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref;
23120 pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect;
23121 pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect;
23122 pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback;
23123 pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state;
23124 pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list;
23125 pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list;
23126 pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name;
23127 pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name;
23128 pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref;
23129 pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state;
23130 pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend;
23131 pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid;
23132 pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible;
23133 pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new;
23134 pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref;
23135 pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback;
23136 pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record;
23137 pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect;
23138 pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state;
23139 pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec;
23140 pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map;
23141 pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr;
23142 pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr;
23143 pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name;
23144 pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback;
23145 pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback;
23146 pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush;
23147 pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain;
23148 pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked;
23149 pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork;
23150 pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger;
23151 pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write;
23152 pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write;
23153 pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek;
23154 pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop;
23155 pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size;
23156 pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size;
23159 /* 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. */
23160 pContext->pulse.pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
23161 if (pContext->pulse.pMainLoop == NULL) {
23162 result = ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop.", MA_FAILED_TO_INIT_BACKEND);
23163 #ifndef MA_NO_RUNTIME_LINKING
23164 ma_dlclose(pContext, pContext->pulse.pulseSO);
23169 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);
23170 if (pContext->pulse.pPulseContext == NULL) {
23171 result = ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context.", MA_FAILED_TO_INIT_BACKEND);
23172 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pContext->pulse.pMainLoop));
23173 #ifndef MA_NO_RUNTIME_LINKING
23174 ma_dlclose(pContext, pContext->pulse.pulseSO);
23179 /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */
23180 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));
23181 if (result != MA_SUCCESS) {
23182 ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context.", result);
23183 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pContext->pulse.pMainLoop));
23184 #ifndef MA_NO_RUNTIME_LINKING
23185 ma_dlclose(pContext, pContext->pulse.pulseSO);
23190 /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */
23191 result = ma_context_wait_for_pa_context_to_connect__pulse(pContext);
23192 if (result != MA_SUCCESS) {
23193 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pContext->pulse.pMainLoop));
23194 #ifndef MA_NO_RUNTIME_LINKING
23195 ma_dlclose(pContext, pContext->pulse.pulseSO);
23201 /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */
23202 pCallbacks->onContextInit = ma_context_init__pulse;
23203 pCallbacks->onContextUninit = ma_context_uninit__pulse;
23204 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse;
23205 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__pulse;
23206 pCallbacks->onDeviceInit = ma_device_init__pulse;
23207 pCallbacks->onDeviceUninit = ma_device_uninit__pulse;
23208 pCallbacks->onDeviceStart = ma_device_start__pulse;
23209 pCallbacks->onDeviceStop = ma_device_stop__pulse;
23210 pCallbacks->onDeviceRead = NULL; /* Not used because we're implementing onDeviceDataLoop. */
23211 pCallbacks->onDeviceWrite = NULL; /* Not used because we're implementing onDeviceDataLoop. */
23212 pCallbacks->onDeviceDataLoop = ma_device_data_loop__pulse;
23213 pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__pulse;
23220 /******************************************************************************
23224 ******************************************************************************/
23227 /* It is assumed jack.h is available when compile-time linking is being used. */
23228 #ifdef MA_NO_RUNTIME_LINKING
23229 #include <jack/jack.h>
23231 typedef jack_nframes_t ma_jack_nframes_t;
23232 typedef jack_options_t ma_jack_options_t;
23233 typedef jack_status_t ma_jack_status_t;
23234 typedef jack_client_t ma_jack_client_t;
23235 typedef jack_port_t ma_jack_port_t;
23236 typedef JackProcessCallback ma_JackProcessCallback;
23237 typedef JackBufferSizeCallback ma_JackBufferSizeCallback;
23238 typedef JackShutdownCallback ma_JackShutdownCallback;
23239 #define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE
23240 #define ma_JackNoStartServer JackNoStartServer
23241 #define ma_JackPortIsInput JackPortIsInput
23242 #define ma_JackPortIsOutput JackPortIsOutput
23243 #define ma_JackPortIsPhysical JackPortIsPhysical
23245 typedef ma_uint32 ma_jack_nframes_t;
23246 typedef int ma_jack_options_t;
23247 typedef int ma_jack_status_t;
23248 typedef struct ma_jack_client_t ma_jack_client_t;
23249 typedef struct ma_jack_port_t ma_jack_port_t;
23250 typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg);
23251 typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg);
23252 typedef void (* ma_JackShutdownCallback) (void* arg);
23253 #define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio"
23254 #define ma_JackNoStartServer 1
23255 #define ma_JackPortIsInput 1
23256 #define ma_JackPortIsOutput 2
23257 #define ma_JackPortIsPhysical 4
23260 typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...);
23261 typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client);
23262 typedef int (* ma_jack_client_name_size_proc) (void);
23263 typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg);
23264 typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg);
23265 typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg);
23266 typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client);
23267 typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client);
23268 typedef const char** (* ma_jack_get_ports_proc) (ma_jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags);
23269 typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client);
23270 typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client);
23271 typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port);
23272 typedef ma_jack_port_t* (* ma_jack_port_register_proc) (ma_jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size);
23273 typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port);
23274 typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes);
23275 typedef void (* ma_jack_free_proc) (void* ptr);
23277 static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient)
23279 size_t maxClientNameSize;
23280 char clientName[256];
23281 ma_jack_status_t status;
23282 ma_jack_client_t* pClient;
23284 MA_ASSERT(pContext != NULL);
23285 MA_ASSERT(ppClient != NULL);
23291 maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */
23292 ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1);
23294 pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL);
23295 if (pClient == NULL) {
23296 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
23300 *ppClient = pClient;
23307 static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
23309 ma_bool32 cbResult = MA_TRUE;
23311 MA_ASSERT(pContext != NULL);
23312 MA_ASSERT(callback != NULL);
23316 ma_device_info deviceInfo;
23317 MA_ZERO_OBJECT(&deviceInfo);
23318 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
23319 deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */
23320 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
23325 ma_device_info deviceInfo;
23326 MA_ZERO_OBJECT(&deviceInfo);
23327 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
23328 deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */
23329 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
23332 (void)cbResult; /* For silencing a static analysis warning. */
23337 static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
23339 ma_jack_client_t* pClient;
23341 const char** ppPorts;
23343 MA_ASSERT(pContext != NULL);
23345 if (pDeviceID != NULL && pDeviceID->jack != 0) {
23346 return MA_NO_DEVICE; /* Don't know the device. */
23349 /* Name / Description */
23350 if (deviceType == ma_device_type_playback) {
23351 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
23353 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
23356 /* Jack only uses default devices. */
23357 pDeviceInfo->isDefault = MA_TRUE;
23359 /* Jack only supports f32 and has a specific channel count and sample rate. */
23360 pDeviceInfo->nativeDataFormats[0].format = ma_format_f32;
23362 /* The channel count and sample rate can only be determined by opening the device. */
23363 result = ma_context_open_client__jack(pContext, &pClient);
23364 if (result != MA_SUCCESS) {
23365 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", result);
23368 pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient);
23369 pDeviceInfo->nativeDataFormats[0].channels = 0;
23371 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));
23372 if (ppPorts == NULL) {
23373 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
23374 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
23377 while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) {
23378 pDeviceInfo->nativeDataFormats[0].channels += 1;
23381 pDeviceInfo->nativeDataFormats[0].flags = 0;
23382 pDeviceInfo->nativeDataFormatCount = 1;
23384 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
23385 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
23392 static ma_result ma_device_uninit__jack(ma_device* pDevice)
23394 ma_context* pContext;
23396 MA_ASSERT(pDevice != NULL);
23398 pContext = pDevice->pContext;
23399 MA_ASSERT(pContext != NULL);
23401 if (pDevice->jack.pClient != NULL) {
23402 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient);
23405 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23406 ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
23409 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23410 ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
23416 static void ma_device__jack_shutdown_callback(void* pUserData)
23418 /* JACK died. Stop the device. */
23419 ma_device* pDevice = (ma_device*)pUserData;
23420 MA_ASSERT(pDevice != NULL);
23422 ma_device_stop(pDevice);
23425 static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData)
23427 ma_device* pDevice = (ma_device*)pUserData;
23428 MA_ASSERT(pDevice != NULL);
23430 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23431 size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
23432 float* pNewBuffer = (float*)ma__calloc_from_callbacks(newBufferSize, &pDevice->pContext->allocationCallbacks);
23433 if (pNewBuffer == NULL) {
23434 return MA_OUT_OF_MEMORY;
23437 ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
23439 pDevice->jack.pIntermediaryBufferCapture = pNewBuffer;
23440 pDevice->playback.internalPeriodSizeInFrames = frameCount;
23443 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23444 size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
23445 float* pNewBuffer = (float*)ma__calloc_from_callbacks(newBufferSize, &pDevice->pContext->allocationCallbacks);
23446 if (pNewBuffer == NULL) {
23447 return MA_OUT_OF_MEMORY;
23450 ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
23452 pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer;
23453 pDevice->playback.internalPeriodSizeInFrames = frameCount;
23459 static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData)
23461 ma_device* pDevice;
23462 ma_context* pContext;
23463 ma_uint32 iChannel;
23465 pDevice = (ma_device*)pUserData;
23466 MA_ASSERT(pDevice != NULL);
23468 pContext = pDevice->pContext;
23469 MA_ASSERT(pContext != NULL);
23471 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23472 /* Channels need to be interleaved. */
23473 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
23474 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);
23475 if (pSrc != NULL) {
23476 float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel;
23477 ma_jack_nframes_t iFrame;
23478 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
23481 pDst += pDevice->capture.internalChannels;
23487 ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount);
23490 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23491 ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount);
23493 /* Channels need to be deinterleaved. */
23494 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
23495 float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.pPortsPlayback[iChannel], frameCount);
23496 if (pDst != NULL) {
23497 const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel;
23498 ma_jack_nframes_t iFrame;
23499 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
23503 pSrc += pDevice->playback.internalChannels;
23512 static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
23515 ma_uint32 periodSizeInFrames;
23517 MA_ASSERT(pConfig != NULL);
23518 MA_ASSERT(pDevice != NULL);
23520 if (pConfig->deviceType == ma_device_type_loopback) {
23521 return MA_DEVICE_TYPE_NOT_SUPPORTED;
23524 /* Only supporting default devices with JACK. */
23525 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) ||
23526 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) {
23527 return MA_NO_DEVICE;
23530 /* No exclusive mode with the JACK backend. */
23531 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
23532 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
23533 return MA_SHARE_MODE_NOT_SUPPORTED;
23536 /* Open the client. */
23537 result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient);
23538 if (result != MA_SUCCESS) {
23539 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", result);
23543 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) {
23544 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
23546 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) {
23547 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
23550 ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice);
23553 /* The buffer size in frames can change. */
23554 periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient);
23556 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
23557 const char** ppPorts;
23559 pDescriptorCapture->format = ma_format_f32;
23560 pDescriptorCapture->channels = 0;
23561 pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
23562 ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
23564 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);
23565 if (ppPorts == NULL) {
23566 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
23569 while (ppPorts[pDescriptorCapture->channels] != NULL) {
23571 ma_strcpy_s(name, sizeof(name), "capture");
23572 ma_itoa_s((int)pDescriptorCapture->channels, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */
23574 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);
23575 if (pDevice->jack.pPortsCapture[pDescriptorCapture->channels] == NULL) {
23576 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
23577 ma_device_uninit__jack(pDevice);
23578 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
23581 pDescriptorCapture->channels += 1;
23584 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
23586 pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
23587 pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */
23589 pDevice->jack.pIntermediaryBufferCapture = (float*)ma__calloc_from_callbacks(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks);
23590 if (pDevice->jack.pIntermediaryBufferCapture == NULL) {
23591 ma_device_uninit__jack(pDevice);
23592 return MA_OUT_OF_MEMORY;
23596 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
23597 const char** ppPorts;
23599 pDescriptorPlayback->format = ma_format_f32;
23600 pDescriptorPlayback->channels = 0;
23601 pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
23602 ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
23604 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);
23605 if (ppPorts == NULL) {
23606 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
23609 while (ppPorts[pDescriptorPlayback->channels] != NULL) {
23611 ma_strcpy_s(name, sizeof(name), "playback");
23612 ma_itoa_s((int)pDescriptorPlayback->channels, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */
23614 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);
23615 if (pDevice->jack.pPortsPlayback[pDescriptorPlayback->channels] == NULL) {
23616 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
23617 ma_device_uninit__jack(pDevice);
23618 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
23621 pDescriptorPlayback->channels += 1;
23624 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
23626 pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
23627 pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */
23629 pDevice->jack.pIntermediaryBufferPlayback = (float*)ma__calloc_from_callbacks(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks);
23630 if (pDevice->jack.pIntermediaryBufferPlayback == NULL) {
23631 ma_device_uninit__jack(pDevice);
23632 return MA_OUT_OF_MEMORY;
23640 static ma_result ma_device_start__jack(ma_device* pDevice)
23642 ma_context* pContext = pDevice->pContext;
23646 resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient);
23647 if (resultJACK != 0) {
23648 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client.", MA_FAILED_TO_START_BACKEND_DEVICE);
23651 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23652 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);
23653 if (ppServerPorts == NULL) {
23654 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
23655 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.", MA_ERROR);
23658 for (i = 0; ppServerPorts[i] != NULL; ++i) {
23659 const char* pServerPort = ppServerPorts[i];
23660 const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.pPortsCapture[i]);
23662 resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort);
23663 if (resultJACK != 0) {
23664 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
23665 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
23666 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.", MA_ERROR);
23670 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
23673 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23674 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);
23675 if (ppServerPorts == NULL) {
23676 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
23677 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.", MA_ERROR);
23680 for (i = 0; ppServerPorts[i] != NULL; ++i) {
23681 const char* pServerPort = ppServerPorts[i];
23682 const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.pPortsPlayback[i]);
23684 resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort);
23685 if (resultJACK != 0) {
23686 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
23687 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
23688 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.", MA_ERROR);
23692 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
23698 static ma_result ma_device_stop__jack(ma_device* pDevice)
23700 ma_context* pContext = pDevice->pContext;
23701 ma_stop_proc onStop;
23703 if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) {
23704 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client.", MA_ERROR);
23707 onStop = pDevice->onStop;
23716 static ma_result ma_context_uninit__jack(ma_context* pContext)
23718 MA_ASSERT(pContext != NULL);
23719 MA_ASSERT(pContext->backend == ma_backend_jack);
23721 ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
23722 pContext->jack.pClientName = NULL;
23724 #ifndef MA_NO_RUNTIME_LINKING
23725 ma_dlclose(pContext, pContext->jack.jackSO);
23731 static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
23733 #ifndef MA_NO_RUNTIME_LINKING
23734 const char* libjackNames[] = {
23745 for (i = 0; i < ma_countof(libjackNames); ++i) {
23746 pContext->jack.jackSO = ma_dlopen(pContext, libjackNames[i]);
23747 if (pContext->jack.jackSO != NULL) {
23752 if (pContext->jack.jackSO == NULL) {
23753 return MA_NO_BACKEND;
23756 pContext->jack.jack_client_open = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_open");
23757 pContext->jack.jack_client_close = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_close");
23758 pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_name_size");
23759 pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_process_callback");
23760 pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_buffer_size_callback");
23761 pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_on_shutdown");
23762 pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_sample_rate");
23763 pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_buffer_size");
23764 pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_ports");
23765 pContext->jack.jack_activate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_activate");
23766 pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_deactivate");
23767 pContext->jack.jack_connect = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_connect");
23768 pContext->jack.jack_port_register = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_register");
23769 pContext->jack.jack_port_name = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_name");
23770 pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_get_buffer");
23771 pContext->jack.jack_free = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_free");
23774 This strange assignment system is here just to ensure type safety of miniaudio's function pointer
23775 types. If anything differs slightly the compiler should throw a warning.
23777 ma_jack_client_open_proc _jack_client_open = jack_client_open;
23778 ma_jack_client_close_proc _jack_client_close = jack_client_close;
23779 ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size;
23780 ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback;
23781 ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback;
23782 ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown;
23783 ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate;
23784 ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size;
23785 ma_jack_get_ports_proc _jack_get_ports = jack_get_ports;
23786 ma_jack_activate_proc _jack_activate = jack_activate;
23787 ma_jack_deactivate_proc _jack_deactivate = jack_deactivate;
23788 ma_jack_connect_proc _jack_connect = jack_connect;
23789 ma_jack_port_register_proc _jack_port_register = jack_port_register;
23790 ma_jack_port_name_proc _jack_port_name = jack_port_name;
23791 ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer;
23792 ma_jack_free_proc _jack_free = jack_free;
23794 pContext->jack.jack_client_open = (ma_proc)_jack_client_open;
23795 pContext->jack.jack_client_close = (ma_proc)_jack_client_close;
23796 pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size;
23797 pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback;
23798 pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback;
23799 pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown;
23800 pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate;
23801 pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size;
23802 pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports;
23803 pContext->jack.jack_activate = (ma_proc)_jack_activate;
23804 pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate;
23805 pContext->jack.jack_connect = (ma_proc)_jack_connect;
23806 pContext->jack.jack_port_register = (ma_proc)_jack_port_register;
23807 pContext->jack.jack_port_name = (ma_proc)_jack_port_name;
23808 pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer;
23809 pContext->jack.jack_free = (ma_proc)_jack_free;
23812 if (pConfig->jack.pClientName != NULL) {
23813 pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks);
23815 pContext->jack.tryStartServer = pConfig->jack.tryStartServer;
23818 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
23819 a temporary client.
23822 ma_jack_client_t* pDummyClient;
23823 ma_result result = ma_context_open_client__jack(pContext, &pDummyClient);
23824 if (result != MA_SUCCESS) {
23825 ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
23826 #ifndef MA_NO_RUNTIME_LINKING
23827 ma_dlclose(pContext, pContext->jack.jackSO);
23829 return MA_NO_BACKEND;
23832 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient);
23836 pCallbacks->onContextInit = ma_context_init__jack;
23837 pCallbacks->onContextUninit = ma_context_uninit__jack;
23838 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack;
23839 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack;
23840 pCallbacks->onDeviceInit = ma_device_init__jack;
23841 pCallbacks->onDeviceUninit = ma_device_uninit__jack;
23842 pCallbacks->onDeviceStart = ma_device_start__jack;
23843 pCallbacks->onDeviceStop = ma_device_stop__jack;
23844 pCallbacks->onDeviceRead = NULL; /* Not used because JACK is asynchronous. */
23845 pCallbacks->onDeviceWrite = NULL; /* Not used because JACK is asynchronous. */
23846 pCallbacks->onDeviceDataLoop = NULL; /* Not used because JACK is asynchronous. */
23854 /******************************************************************************
23860 - Technical Note TN2091: Device input using the HAL Output Audio Unit
23861 https://developer.apple.com/library/archive/technotes/tn2091/_index.html
23863 ******************************************************************************/
23864 #ifdef MA_HAS_COREAUDIO
23865 #include <TargetConditionals.h>
23867 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1
23868 #define MA_APPLE_MOBILE
23869 #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
23870 #define MA_APPLE_TV
23872 #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
23873 #define MA_APPLE_WATCH
23876 #define MA_APPLE_DESKTOP
23879 #if defined(MA_APPLE_DESKTOP)
23880 #include <CoreAudio/CoreAudio.h>
23882 #include <AVFoundation/AVFoundation.h>
23885 #include <AudioToolbox/AudioToolbox.h>
23887 /* CoreFoundation */
23888 typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding);
23889 typedef void (* ma_CFRelease_proc)(CFTypeRef cf);
23892 #if defined(MA_APPLE_DESKTOP)
23893 typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData);
23894 typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize);
23895 typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
23896 typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
23897 typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
23901 typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc);
23902 typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance);
23903 typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance);
23904 typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit);
23905 typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit);
23906 typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData);
23907 typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable);
23908 typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize);
23909 typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize);
23910 typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit);
23911 typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);
23914 #define MA_COREAUDIO_OUTPUT_BUS 0
23915 #define MA_COREAUDIO_INPUT_BUS 1
23917 #if defined(MA_APPLE_DESKTOP)
23918 static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit);
23924 So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation
23925 apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose
23926 needing to figure out how this darn thing works, I'm going to outline a few things here.
23928 Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be
23929 able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen
23930 that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent
23931 and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the
23932 distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API.
23934 Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When
23935 retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific
23936 data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the
23937 devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be
23938 the central APIs for retrieving information about the system and specific devices.
23940 To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a
23941 structure with three variables and is used to identify which property you are getting or setting. The first is the "selector"
23942 which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is
23943 typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and
23944 kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to
23945 kAudioObjectPropertyElementMaster in miniaudio's case. I don't know of any cases where this would be set to anything different.
23947 Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size
23948 of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property
23949 address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the
23950 size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of
23951 AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count.
23954 static ma_result ma_result_from_OSStatus(OSStatus status)
23958 case noErr: return MA_SUCCESS;
23959 #if defined(MA_APPLE_DESKTOP)
23960 case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED;
23961 case kAudioHardwareUnspecifiedError: return MA_ERROR;
23962 case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS;
23963 case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION;
23964 case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION;
23965 case kAudioHardwareBadObjectError: return MA_INVALID_ARGS;
23966 case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS;
23967 case kAudioHardwareBadStreamError: return MA_INVALID_ARGS;
23968 case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION;
23969 case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED;
23970 case kAudioDevicePermissionsError: return MA_ACCESS_DENIED;
23972 default: return MA_ERROR;
23977 static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit)
23981 case kAudioChannelBit_Left: return MA_CHANNEL_LEFT;
23982 case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT;
23983 case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER;
23984 case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE;
23985 case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT;
23986 case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT;
23987 case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
23988 case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
23989 case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER;
23990 case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
23991 case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
23992 case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
23993 case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
23994 case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
23995 case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
23996 case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
23997 case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
23998 case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
23999 default: return MA_CHANNEL_NONE;
24004 static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut)
24006 MA_ASSERT(pDescription != NULL);
24007 MA_ASSERT(pFormatOut != NULL);
24009 *pFormatOut = ma_format_unknown; /* Safety. */
24011 /* There's a few things miniaudio doesn't support. */
24012 if (pDescription->mFormatID != kAudioFormatLinearPCM) {
24013 return MA_FORMAT_NOT_SUPPORTED;
24016 /* We don't support any non-packed formats that are aligned high. */
24017 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) {
24018 return MA_FORMAT_NOT_SUPPORTED;
24021 /* Only supporting native-endian. */
24022 if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) {
24023 return MA_FORMAT_NOT_SUPPORTED;
24026 /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */
24027 /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) {
24028 return MA_FORMAT_NOT_SUPPORTED;
24031 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) {
24032 if (pDescription->mBitsPerChannel == 32) {
24033 *pFormatOut = ma_format_f32;
24037 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) {
24038 if (pDescription->mBitsPerChannel == 16) {
24039 *pFormatOut = ma_format_s16;
24041 } else if (pDescription->mBitsPerChannel == 24) {
24042 if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) {
24043 *pFormatOut = ma_format_s24;
24046 if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) {
24047 /* TODO: Implement ma_format_s24_32. */
24048 /**pFormatOut = ma_format_s24_32;*/
24049 /*return MA_SUCCESS;*/
24050 return MA_FORMAT_NOT_SUPPORTED;
24053 } else if (pDescription->mBitsPerChannel == 32) {
24054 *pFormatOut = ma_format_s32;
24058 if (pDescription->mBitsPerChannel == 8) {
24059 *pFormatOut = ma_format_u8;
24065 /* Getting here means the format is not supported. */
24066 return MA_FORMAT_NOT_SUPPORTED;
24069 #if defined(MA_APPLE_DESKTOP)
24070 static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label)
24074 case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE;
24075 case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE;
24076 case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE;
24077 case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT;
24078 case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT;
24079 case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER;
24080 case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE;
24081 case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT;
24082 case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT;
24083 case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
24084 case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
24085 case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER;
24086 case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
24087 case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
24088 case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
24089 case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
24090 case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
24091 case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
24092 case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
24093 case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
24094 case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
24095 case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT;
24096 case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT;
24097 case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT;
24098 case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT;
24099 case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE;
24100 case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT;
24101 case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT;
24102 case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE;
24103 case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO;
24104 case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO;
24105 case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO;
24106 case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER;
24107 case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE;
24108 case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE;
24109 case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE;
24110 case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE;
24111 case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE;
24112 case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT;
24113 case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT;
24114 case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT;
24115 case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT;
24116 case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT;
24117 case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT;
24118 case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE;
24119 case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE;
24120 case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE;
24121 case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0;
24122 case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1;
24123 case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2;
24124 case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3;
24125 case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4;
24126 case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5;
24127 case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6;
24128 case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7;
24129 case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8;
24130 case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9;
24131 case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10;
24132 case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11;
24133 case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12;
24134 case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13;
24135 case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14;
24136 case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15;
24137 case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE;
24139 #if 0 /* Introduced in a later version of macOS. */
24140 case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE;
24141 case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0;
24142 case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1;
24143 case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2;
24144 case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3;
24145 case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4;
24146 case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5;
24147 case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6;
24148 case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7;
24149 case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8;
24150 case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9;
24151 case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10;
24152 case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11;
24153 case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12;
24154 case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13;
24155 case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14;
24156 case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15;
24157 case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE;
24160 default: return MA_CHANNEL_NONE;
24164 static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap)
24166 MA_ASSERT(pChannelLayout != NULL);
24168 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
24170 for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) {
24171 pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel);
24175 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
24176 /* This is the same kind of system that's used by Windows audio APIs. */
24177 UInt32 iChannel = 0;
24179 AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap;
24180 for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) {
24181 AudioChannelBitmap bit = bitmap & (1 << iBit);
24183 pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit);
24190 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
24191 be updated to determine the mapping based on the tag.
24193 UInt32 channelCount;
24195 /* Our channel map retrieval APIs below take 32-bit integers, so we'll want to clamp the channel map capacity. */
24196 if (channelMapCap > 0xFFFFFFFF) {
24197 channelMapCap = 0xFFFFFFFF;
24200 channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap);
24202 switch (pChannelLayout->mChannelLayoutTag)
24204 case kAudioChannelLayoutTag_Mono:
24205 case kAudioChannelLayoutTag_Stereo:
24206 case kAudioChannelLayoutTag_StereoHeadphones:
24207 case kAudioChannelLayoutTag_MatrixStereo:
24208 case kAudioChannelLayoutTag_MidSide:
24209 case kAudioChannelLayoutTag_XY:
24210 case kAudioChannelLayoutTag_Binaural:
24211 case kAudioChannelLayoutTag_Ambisonic_B_Format:
24213 ma_get_standard_channel_map(ma_standard_channel_map_default, channelCount, pChannelMap);
24216 case kAudioChannelLayoutTag_Octagonal:
24218 pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
24219 pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
24220 } /* Intentional fallthrough. */
24221 case kAudioChannelLayoutTag_Hexagonal:
24223 pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
24224 } /* Intentional fallthrough. */
24225 case kAudioChannelLayoutTag_Pentagonal:
24227 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
24228 } /* Intentional fallghrough. */
24229 case kAudioChannelLayoutTag_Quadraphonic:
24231 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
24232 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
24233 pChannelMap[1] = MA_CHANNEL_RIGHT;
24234 pChannelMap[0] = MA_CHANNEL_LEFT;
24237 /* TODO: Add support for more tags here. */
24241 ma_get_standard_channel_map(ma_standard_channel_map_default, channelCount, pChannelMap);
24249 static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */
24251 AudioObjectPropertyAddress propAddressDevices;
24252 UInt32 deviceObjectsDataSize;
24254 AudioObjectID* pDeviceObjectIDs;
24256 MA_ASSERT(pContext != NULL);
24257 MA_ASSERT(pDeviceCount != NULL);
24258 MA_ASSERT(ppDeviceObjectIDs != NULL);
24262 *ppDeviceObjectIDs = NULL;
24264 propAddressDevices.mSelector = kAudioHardwarePropertyDevices;
24265 propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal;
24266 propAddressDevices.mElement = kAudioObjectPropertyElementMaster;
24268 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize);
24269 if (status != noErr) {
24270 return ma_result_from_OSStatus(status);
24273 pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks);
24274 if (pDeviceObjectIDs == NULL) {
24275 return MA_OUT_OF_MEMORY;
24278 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs);
24279 if (status != noErr) {
24280 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
24281 return ma_result_from_OSStatus(status);
24284 *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID);
24285 *ppDeviceObjectIDs = pDeviceObjectIDs;
24290 static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID)
24292 AudioObjectPropertyAddress propAddress;
24296 MA_ASSERT(pContext != NULL);
24298 propAddress.mSelector = kAudioDevicePropertyDeviceUID;
24299 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
24300 propAddress.mElement = kAudioObjectPropertyElementMaster;
24302 dataSize = sizeof(*pUID);
24303 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID);
24304 if (status != noErr) {
24305 return ma_result_from_OSStatus(status);
24311 static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
24316 MA_ASSERT(pContext != NULL);
24318 result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid);
24319 if (result != MA_SUCCESS) {
24323 if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
24327 ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid);
24331 static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
24333 AudioObjectPropertyAddress propAddress;
24334 CFStringRef deviceName = NULL;
24338 MA_ASSERT(pContext != NULL);
24340 propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
24341 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
24342 propAddress.mElement = kAudioObjectPropertyElementMaster;
24344 dataSize = sizeof(deviceName);
24345 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName);
24346 if (status != noErr) {
24347 return ma_result_from_OSStatus(status);
24350 if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
24354 ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName);
24358 static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope)
24360 AudioObjectPropertyAddress propAddress;
24363 AudioBufferList* pBufferList;
24364 ma_bool32 isSupported;
24366 MA_ASSERT(pContext != NULL);
24368 /* 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. */
24369 propAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
24370 propAddress.mScope = scope;
24371 propAddress.mElement = kAudioObjectPropertyElementMaster;
24373 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
24374 if (status != noErr) {
24378 pBufferList = (AudioBufferList*)ma__malloc_from_callbacks(dataSize, &pContext->allocationCallbacks);
24379 if (pBufferList == NULL) {
24380 return MA_FALSE; /* Out of memory. */
24383 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList);
24384 if (status != noErr) {
24385 ma__free_from_callbacks(pBufferList, &pContext->allocationCallbacks);
24389 isSupported = MA_FALSE;
24390 if (pBufferList->mNumberBuffers > 0) {
24391 isSupported = MA_TRUE;
24394 ma__free_from_callbacks(pBufferList, &pContext->allocationCallbacks);
24395 return isSupported;
24398 static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID)
24400 return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput);
24403 static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID)
24405 return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput);
24409 static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pDescriptionCount, AudioStreamRangedDescription** ppDescriptions) /* NOTE: Free the returned pointer with ma_free(). */
24411 AudioObjectPropertyAddress propAddress;
24414 AudioStreamRangedDescription* pDescriptions;
24416 MA_ASSERT(pContext != NULL);
24417 MA_ASSERT(pDescriptionCount != NULL);
24418 MA_ASSERT(ppDescriptions != NULL);
24421 TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My
24422 MacBook Pro uses s24/32 format, however, which miniaudio does not currently support.
24424 propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/
24425 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
24426 propAddress.mElement = kAudioObjectPropertyElementMaster;
24428 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
24429 if (status != noErr) {
24430 return ma_result_from_OSStatus(status);
24433 pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks);
24434 if (pDescriptions == NULL) {
24435 return MA_OUT_OF_MEMORY;
24438 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions);
24439 if (status != noErr) {
24440 ma_free(pDescriptions, &pContext->allocationCallbacks);
24441 return ma_result_from_OSStatus(status);
24444 *pDescriptionCount = dataSize / sizeof(*pDescriptions);
24445 *ppDescriptions = pDescriptions;
24450 static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, AudioChannelLayout** ppChannelLayout) /* NOTE: Free the returned pointer with ma_free(). */
24452 AudioObjectPropertyAddress propAddress;
24455 AudioChannelLayout* pChannelLayout;
24457 MA_ASSERT(pContext != NULL);
24458 MA_ASSERT(ppChannelLayout != NULL);
24460 *ppChannelLayout = NULL; /* Safety. */
24462 propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
24463 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
24464 propAddress.mElement = kAudioObjectPropertyElementMaster;
24466 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
24467 if (status != noErr) {
24468 return ma_result_from_OSStatus(status);
24471 pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks);
24472 if (pChannelLayout == NULL) {
24473 return MA_OUT_OF_MEMORY;
24476 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout);
24477 if (status != noErr) {
24478 ma_free(pChannelLayout, &pContext->allocationCallbacks);
24479 return ma_result_from_OSStatus(status);
24482 *ppChannelLayout = pChannelLayout;
24486 static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount)
24488 AudioChannelLayout* pChannelLayout;
24491 MA_ASSERT(pContext != NULL);
24492 MA_ASSERT(pChannelCount != NULL);
24494 *pChannelCount = 0; /* Safety. */
24496 result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
24497 if (result != MA_SUCCESS) {
24501 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
24502 *pChannelCount = pChannelLayout->mNumberChannelDescriptions;
24503 } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
24504 *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap);
24506 *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);
24509 ma_free(pChannelLayout, &pContext->allocationCallbacks);
24514 static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
24516 AudioChannelLayout* pChannelLayout;
24519 MA_ASSERT(pContext != NULL);
24521 result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
24522 if (result != MA_SUCCESS) {
24523 return result; /* Rather than always failing here, would it be more robust to simply assume a default? */
24526 result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
24527 if (result != MA_SUCCESS) {
24528 ma_free(pChannelLayout, &pContext->allocationCallbacks);
24532 ma_free(pChannelLayout, &pContext->allocationCallbacks);
24537 static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges) /* NOTE: Free the returned pointer with ma_free(). */
24539 AudioObjectPropertyAddress propAddress;
24542 AudioValueRange* pSampleRateRanges;
24544 MA_ASSERT(pContext != NULL);
24545 MA_ASSERT(pSampleRateRangesCount != NULL);
24546 MA_ASSERT(ppSampleRateRanges != NULL);
24549 *pSampleRateRangesCount = 0;
24550 *ppSampleRateRanges = NULL;
24552 propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
24553 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
24554 propAddress.mElement = kAudioObjectPropertyElementMaster;
24556 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
24557 if (status != noErr) {
24558 return ma_result_from_OSStatus(status);
24561 pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks);
24562 if (pSampleRateRanges == NULL) {
24563 return MA_OUT_OF_MEMORY;
24566 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges);
24567 if (status != noErr) {
24568 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
24569 return ma_result_from_OSStatus(status);
24572 *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges);
24573 *ppSampleRateRanges = pSampleRateRanges;
24578 static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut)
24580 UInt32 sampleRateRangeCount;
24581 AudioValueRange* pSampleRateRanges;
24584 MA_ASSERT(pContext != NULL);
24585 MA_ASSERT(pSampleRateOut != NULL);
24587 *pSampleRateOut = 0; /* Safety. */
24589 result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
24590 if (result != MA_SUCCESS) {
24594 if (sampleRateRangeCount == 0) {
24595 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
24596 return MA_ERROR; /* Should never hit this case should we? */
24599 if (sampleRateIn == 0) {
24600 /* Search in order of miniaudio's preferred priority. */
24601 UInt32 iMALSampleRate;
24602 for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) {
24603 ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate];
24604 UInt32 iCASampleRate;
24605 for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) {
24606 AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate];
24607 if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) {
24608 *pSampleRateOut = malSampleRate;
24609 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
24616 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
24617 case we just fall back to the first one reported by Core Audio.
24619 MA_ASSERT(sampleRateRangeCount > 0);
24621 *pSampleRateOut = pSampleRateRanges[0].mMinimum;
24622 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
24625 /* Find the closest match to this sample rate. */
24626 UInt32 currentAbsoluteDifference = INT32_MAX;
24627 UInt32 iCurrentClosestRange = (UInt32)-1;
24629 for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) {
24630 if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) {
24631 *pSampleRateOut = sampleRateIn;
24632 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
24635 UInt32 absoluteDifference;
24636 if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) {
24637 absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn;
24639 absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum;
24642 if (currentAbsoluteDifference > absoluteDifference) {
24643 currentAbsoluteDifference = absoluteDifference;
24644 iCurrentClosestRange = iRange;
24649 MA_ASSERT(iCurrentClosestRange != (UInt32)-1);
24651 *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum;
24652 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
24656 /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */
24657 /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/
24658 /*return MA_ERROR;*/
24662 static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 bufferSizeInFramesIn, ma_uint32* pBufferSizeInFramesOut)
24664 AudioObjectPropertyAddress propAddress;
24665 AudioValueRange bufferSizeRange;
24669 MA_ASSERT(pContext != NULL);
24670 MA_ASSERT(pBufferSizeInFramesOut != NULL);
24672 *pBufferSizeInFramesOut = 0; /* Safety. */
24674 propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
24675 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
24676 propAddress.mElement = kAudioObjectPropertyElementMaster;
24678 dataSize = sizeof(bufferSizeRange);
24679 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange);
24680 if (status != noErr) {
24681 return ma_result_from_OSStatus(status);
24684 /* This is just a clamp. */
24685 if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) {
24686 *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum;
24687 } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) {
24688 *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum;
24690 *pBufferSizeInFramesOut = bufferSizeInFramesIn;
24696 static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut)
24699 ma_uint32 chosenBufferSizeInFrames;
24700 AudioObjectPropertyAddress propAddress;
24704 MA_ASSERT(pContext != NULL);
24706 result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames);
24707 if (result != MA_SUCCESS) {
24711 /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */
24712 propAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
24713 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
24714 propAddress.mElement = kAudioObjectPropertyElementMaster;
24716 ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames);
24718 /* Get the actual size of the buffer. */
24719 dataSize = sizeof(*pPeriodSizeInOut);
24720 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames);
24721 if (status != noErr) {
24722 return ma_result_from_OSStatus(status);
24725 *pPeriodSizeInOut = chosenBufferSizeInFrames;
24729 static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID)
24731 AudioObjectPropertyAddress propAddressDefaultDevice;
24732 UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
24733 AudioObjectID defaultDeviceObjectID;
24736 MA_ASSERT(pContext != NULL);
24737 MA_ASSERT(pDeviceObjectID != NULL);
24740 *pDeviceObjectID = 0;
24742 propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal;
24743 propAddressDefaultDevice.mElement = kAudioObjectPropertyElementMaster;
24744 if (deviceType == ma_device_type_playback) {
24745 propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
24747 propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice;
24750 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
24751 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID);
24752 if (status == noErr) {
24753 *pDeviceObjectID = defaultDeviceObjectID;
24757 /* If we get here it means we couldn't find the device. */
24758 return MA_NO_DEVICE;
24761 static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID)
24763 MA_ASSERT(pContext != NULL);
24764 MA_ASSERT(pDeviceObjectID != NULL);
24767 *pDeviceObjectID = 0;
24769 if (pDeviceID == NULL) {
24770 /* Default device. */
24771 return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID);
24773 /* Explicit device. */
24774 UInt32 deviceCount;
24775 AudioObjectID* pDeviceObjectIDs;
24779 result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
24780 if (result != MA_SUCCESS) {
24784 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
24785 AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
24788 if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) {
24792 if (deviceType == ma_device_type_playback) {
24793 if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
24794 if (strcmp(uid, pDeviceID->coreaudio) == 0) {
24795 *pDeviceObjectID = deviceObjectID;
24796 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
24801 if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
24802 if (strcmp(uid, pDeviceID->coreaudio) == 0) {
24803 *pDeviceObjectID = deviceObjectID;
24804 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
24811 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
24814 /* If we get here it means we couldn't find the device. */
24815 return MA_NO_DEVICE;
24819 static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const AudioStreamBasicDescription* pOrigFormat, AudioStreamBasicDescription* pFormat)
24821 UInt32 deviceFormatDescriptionCount;
24822 AudioStreamRangedDescription* pDeviceFormatDescriptions;
24824 ma_uint32 desiredSampleRate;
24825 ma_uint32 desiredChannelCount;
24826 ma_format desiredFormat;
24827 AudioStreamBasicDescription bestDeviceFormatSoFar;
24828 ma_bool32 hasSupportedFormat;
24831 result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions);
24832 if (result != MA_SUCCESS) {
24836 desiredSampleRate = sampleRate;
24837 if (desiredSampleRate == 0) {
24838 desiredSampleRate = pOrigFormat->mSampleRate;
24841 desiredChannelCount = channels;
24842 if (desiredChannelCount == 0) {
24843 desiredChannelCount = pOrigFormat->mChannelsPerFrame;
24846 desiredFormat = format;
24847 if (desiredFormat == ma_format_unknown) {
24848 result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat);
24849 if (result != MA_SUCCESS || desiredFormat == ma_format_unknown) {
24850 desiredFormat = g_maFormatPriorities[0];
24855 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
24856 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.
24858 MA_ZERO_OBJECT(&bestDeviceFormatSoFar);
24860 hasSupportedFormat = MA_FALSE;
24861 for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
24863 ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &format);
24864 if (formatResult == MA_SUCCESS && format != ma_format_unknown) {
24865 hasSupportedFormat = MA_TRUE;
24866 bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat;
24871 if (!hasSupportedFormat) {
24872 ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
24873 return MA_FORMAT_NOT_SUPPORTED;
24877 for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
24878 AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat;
24879 ma_format thisSampleFormat;
24880 ma_result formatResult;
24881 ma_format bestSampleFormatSoFar;
24883 /* If the format is not supported by miniaudio we need to skip this one entirely. */
24884 formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat);
24885 if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) {
24886 continue; /* The format is not supported by miniaudio. Skip. */
24889 ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar);
24891 /* Getting here means the format is supported by miniaudio which makes this format a candidate. */
24892 if (thisDeviceFormat.mSampleRate != desiredSampleRate) {
24894 The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format
24895 so far has an equal sample rate we can just ignore this one.
24897 if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) {
24898 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. */
24900 /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */
24901 if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) {
24902 /* This format has a different sample rate _and_ a different channel count. */
24903 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
24904 continue; /* No change to the best format. */
24907 Both this format and the best so far have different sample rates and different channel counts. Whichever has the
24908 best format is the new best.
24910 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
24911 bestDeviceFormatSoFar = thisDeviceFormat;
24914 continue; /* No change to the best format. */
24918 /* This format has a different sample rate but the desired channel count. */
24919 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
24920 /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */
24921 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
24922 bestDeviceFormatSoFar = thisDeviceFormat;
24925 continue; /* No change to the best format for now. */
24928 /* This format has the desired channel count, but the best so far does not. We have a new best. */
24929 bestDeviceFormatSoFar = thisDeviceFormat;
24936 The sample rates match which makes this format a very high priority contender. If the best format so far has a different
24937 sample rate it needs to be replaced with this one.
24939 if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) {
24940 bestDeviceFormatSoFar = thisDeviceFormat;
24943 /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */
24944 if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) {
24946 In this case this format has the same channel count as what the client is requesting. If the best format so far has
24947 a different count, this one becomes the new best.
24949 if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) {
24950 bestDeviceFormatSoFar = thisDeviceFormat;
24953 /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */
24954 if (thisSampleFormat == desiredFormat) {
24955 bestDeviceFormatSoFar = thisDeviceFormat;
24956 break; /* Found the exact match. */
24958 /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */
24959 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
24960 bestDeviceFormatSoFar = thisDeviceFormat;
24963 continue; /* No change to the best format for now. */
24969 In this case the channel count is different to what the client has requested. If the best so far has the same channel
24970 count as the requested count then it remains the best.
24972 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
24976 This is the case where both have the same sample rate (good) but different channel counts. Right now both have about
24977 the same priority, but we need to compare the format now.
24979 if (thisSampleFormat == bestSampleFormatSoFar) {
24980 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
24981 bestDeviceFormatSoFar = thisDeviceFormat;
24984 continue; /* No change to the best format for now. */
24993 *pFormat = bestDeviceFormatSoFar;
24995 ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
24999 static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
25001 AudioUnitScope deviceScope;
25002 AudioUnitElement deviceBus;
25003 UInt32 channelLayoutSize;
25005 AudioChannelLayout* pChannelLayout;
25008 MA_ASSERT(pContext != NULL);
25010 if (deviceType == ma_device_type_playback) {
25011 deviceScope = kAudioUnitScope_Input;
25012 deviceBus = MA_COREAUDIO_OUTPUT_BUS;
25014 deviceScope = kAudioUnitScope_Output;
25015 deviceBus = MA_COREAUDIO_INPUT_BUS;
25018 status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL);
25019 if (status != noErr) {
25020 return ma_result_from_OSStatus(status);
25023 pChannelLayout = (AudioChannelLayout*)ma__malloc_from_callbacks(channelLayoutSize, &pContext->allocationCallbacks);
25024 if (pChannelLayout == NULL) {
25025 return MA_OUT_OF_MEMORY;
25028 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize);
25029 if (status != noErr) {
25030 ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks);
25031 return ma_result_from_OSStatus(status);
25034 result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
25035 if (result != MA_SUCCESS) {
25036 ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks);
25040 ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks);
25043 #endif /* MA_APPLE_DESKTOP */
25046 #if !defined(MA_APPLE_DESKTOP)
25047 static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo)
25049 MA_ZERO_OBJECT(pInfo);
25050 ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1);
25051 ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1);
25055 static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
25057 #if defined(MA_APPLE_DESKTOP)
25058 UInt32 deviceCount;
25059 AudioObjectID* pDeviceObjectIDs;
25060 AudioObjectID defaultDeviceObjectIDPlayback;
25061 AudioObjectID defaultDeviceObjectIDCapture;
25065 ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */
25066 ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */
25068 result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
25069 if (result != MA_SUCCESS) {
25073 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
25074 AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
25075 ma_device_info info;
25077 MA_ZERO_OBJECT(&info);
25078 if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) {
25081 if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) {
25085 if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
25086 if (deviceObjectID == defaultDeviceObjectIDPlayback) {
25087 info.isDefault = MA_TRUE;
25090 if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
25094 if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
25095 if (deviceObjectID == defaultDeviceObjectIDCapture) {
25096 info.isDefault = MA_TRUE;
25099 if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
25105 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
25107 ma_device_info info;
25108 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
25109 NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
25111 for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
25112 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
25113 if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
25118 for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
25119 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
25120 if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
25129 static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
25133 MA_ASSERT(pContext != NULL);
25135 #if defined(MA_APPLE_DESKTOP)
25138 AudioObjectID deviceObjectID;
25139 AudioObjectID defaultDeviceObjectID;
25140 UInt32 streamDescriptionCount;
25141 AudioStreamRangedDescription* pStreamDescriptions;
25142 UInt32 iStreamDescription;
25143 UInt32 sampleRateRangeCount;
25144 AudioValueRange* pSampleRateRanges;
25146 ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID); /* OK if this fails. */
25148 result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
25149 if (result != MA_SUCCESS) {
25153 result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio);
25154 if (result != MA_SUCCESS) {
25158 result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name);
25159 if (result != MA_SUCCESS) {
25163 if (deviceObjectID == defaultDeviceObjectID) {
25164 pDeviceInfo->isDefault = MA_TRUE;
25168 There could be a large number of permutations here. Fortunately there is only a single channel count
25169 being reported which reduces this quite a bit. For sample rates we're only reporting those that are
25170 one of miniaudio's recognized "standard" rates. If there are still more formats than can fit into
25171 our fixed sized array we'll just need to truncate them. This is unlikely and will probably only happen
25172 if some driver performs software data conversion and therefore reports every possible format and
25175 pDeviceInfo->nativeDataFormatCount = 0;
25179 ma_format uniqueFormats[ma_format_count];
25180 ma_uint32 uniqueFormatCount = 0;
25181 ma_uint32 channels;
25184 result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels);
25185 if (result != MA_SUCCESS) {
25190 result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions);
25191 if (result != MA_SUCCESS) {
25195 for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) {
25197 ma_bool32 hasFormatBeenHandled = MA_FALSE;
25198 ma_uint32 iOutputFormat;
25199 ma_uint32 iSampleRate;
25201 result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format);
25202 if (result != MA_SUCCESS) {
25206 MA_ASSERT(format != ma_format_unknown);
25208 /* Make sure the format isn't already in the output list. */
25209 for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) {
25210 if (uniqueFormats[iOutputFormat] == format) {
25211 hasFormatBeenHandled = MA_TRUE;
25216 /* If we've already handled this format just skip it. */
25217 if (hasFormatBeenHandled) {
25221 uniqueFormatCount += 1;
25225 result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
25226 if (result != MA_SUCCESS) {
25231 Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are
25232 between this range.
25234 for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) {
25235 ma_uint32 iStandardSampleRate;
25236 for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {
25237 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];
25238 if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) {
25239 /* We have a new data format. Add it to the list. */
25240 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
25241 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
25242 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate;
25243 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
25244 pDeviceInfo->nativeDataFormatCount += 1;
25246 if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
25247 break; /* No more room for any more formats. */
25253 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
25255 if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
25256 break; /* No more room for any more formats. */
25260 ma_free(pStreamDescriptions, &pContext->allocationCallbacks);
25266 AudioComponentDescription desc;
25267 AudioComponent component;
25268 AudioUnit audioUnit;
25270 AudioUnitScope formatScope;
25271 AudioUnitElement formatElement;
25272 AudioStreamBasicDescription bestFormat;
25275 /* We want to ensure we use a consistent device name to device enumeration. */
25276 if (pDeviceID != NULL) {
25277 ma_bool32 found = MA_FALSE;
25278 if (deviceType == ma_device_type_playback) {
25279 NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
25280 for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
25281 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
25282 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
25288 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
25289 for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
25290 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
25291 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
25299 return MA_DOES_NOT_EXIST;
25302 if (deviceType == ma_device_type_playback) {
25303 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
25305 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
25311 Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is
25312 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
25313 retrieve from the AVAudioSession shared instance.
25315 desc.componentType = kAudioUnitType_Output;
25316 desc.componentSubType = kAudioUnitSubType_RemoteIO;
25317 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
25318 desc.componentFlags = 0;
25319 desc.componentFlagsMask = 0;
25321 component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
25322 if (component == NULL) {
25323 return MA_FAILED_TO_INIT_BACKEND;
25326 status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit);
25327 if (status != noErr) {
25328 return ma_result_from_OSStatus(status);
25331 formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
25332 formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
25334 propSize = sizeof(bestFormat);
25335 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
25336 if (status != noErr) {
25337 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
25338 return ma_result_from_OSStatus(status);
25341 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
25344 /* Only a single format is being reported for iOS. */
25345 pDeviceInfo->nativeDataFormatCount = 1;
25347 result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format);
25348 if (result != MA_SUCCESS) {
25352 pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame;
25355 It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do
25356 this we just get the shared instance and inspect.
25359 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
25360 MA_ASSERT(pAudioSession != NULL);
25362 pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate;
25367 (void)pDeviceInfo; /* Unused. */
25371 static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks)
25373 AudioBufferList* pBufferList;
25374 UInt32 audioBufferSizeInBytes;
25375 size_t allocationSize;
25377 MA_ASSERT(sizeInFrames > 0);
25378 MA_ASSERT(format != ma_format_unknown);
25379 MA_ASSERT(channels > 0);
25381 allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */
25382 if (layout == ma_stream_layout_interleaved) {
25383 /* Interleaved case. This is the simple case because we just have one buffer. */
25384 allocationSize += sizeof(AudioBuffer) * 1;
25386 /* Non-interleaved case. This is the more complex case because there's more than one buffer. */
25387 allocationSize += sizeof(AudioBuffer) * channels;
25390 allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels);
25392 pBufferList = (AudioBufferList*)ma__malloc_from_callbacks(allocationSize, pAllocationCallbacks);
25393 if (pBufferList == NULL) {
25397 audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format));
25399 if (layout == ma_stream_layout_interleaved) {
25400 pBufferList->mNumberBuffers = 1;
25401 pBufferList->mBuffers[0].mNumberChannels = channels;
25402 pBufferList->mBuffers[0].mDataByteSize = audioBufferSizeInBytes * channels;
25403 pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList);
25406 pBufferList->mNumberBuffers = channels;
25407 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
25408 pBufferList->mBuffers[iBuffer].mNumberChannels = 1;
25409 pBufferList->mBuffers[iBuffer].mDataByteSize = audioBufferSizeInBytes;
25410 pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer);
25414 return pBufferList;
25417 static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout)
25419 MA_ASSERT(pDevice != NULL);
25420 MA_ASSERT(format != ma_format_unknown);
25421 MA_ASSERT(channels > 0);
25423 /* Only resize the buffer if necessary. */
25424 if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) {
25425 AudioBufferList* pNewAudioBufferList;
25427 pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks);
25428 if (pNewAudioBufferList != NULL) {
25429 return MA_OUT_OF_MEMORY;
25432 /* At this point we'll have a new AudioBufferList and we can free the old one. */
25433 ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
25434 pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList;
25435 pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames;
25438 /* Getting here means the capacity of the audio is fine. */
25443 static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList)
25445 ma_device* pDevice = (ma_device*)pUserData;
25446 ma_stream_layout layout;
25448 MA_ASSERT(pDevice != NULL);
25450 #if defined(MA_DEBUG_OUTPUT)
25451 printf("INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pBufferList->mNumberBuffers);
25454 /* 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. */
25455 layout = ma_stream_layout_interleaved;
25456 if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) {
25457 layout = ma_stream_layout_deinterleaved;
25460 if (layout == ma_stream_layout_interleaved) {
25461 /* For now we can assume everything is interleaved. */
25463 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
25464 if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) {
25465 ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
25466 if (frameCountForThisBuffer > 0) {
25467 ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer);
25470 #if defined(MA_DEBUG_OUTPUT)
25471 printf(" frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
25475 This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
25476 not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just
25477 output silence here.
25479 MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
25481 #if defined(MA_DEBUG_OUTPUT)
25482 printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
25487 /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */
25488 MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should heve been validated at initialization time. */
25491 For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
25492 very strange has happened and we're not going to support it.
25494 if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) {
25495 ma_uint8 tempBuffer[4096];
25498 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) {
25499 ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat);
25500 ma_uint32 framesRemaining = frameCountPerBuffer;
25502 while (framesRemaining > 0) {
25503 void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
25504 ma_uint32 iChannel;
25505 ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
25506 if (framesToRead > framesRemaining) {
25507 framesToRead = framesRemaining;
25510 ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead);
25512 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
25513 ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
25516 ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);
25518 framesRemaining -= framesToRead;
25524 (void)pActionFlags;
25532 static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList)
25534 ma_device* pDevice = (ma_device*)pUserData;
25535 AudioBufferList* pRenderedBufferList;
25537 ma_stream_layout layout;
25541 MA_ASSERT(pDevice != NULL);
25543 pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
25544 MA_ASSERT(pRenderedBufferList);
25546 /* 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. */
25547 layout = ma_stream_layout_interleaved;
25548 if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) {
25549 layout = ma_stream_layout_deinterleaved;
25552 #if defined(MA_DEBUG_OUTPUT)
25553 printf("INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers);
25557 There has been a situation reported where frame count passed into this function is greater than the capacity of
25558 our capture buffer. There doesn't seem to be a reliable way to determine what the maximum frame count will be,
25559 so we need to instead resort to dynamically reallocating our buffer to ensure it's large enough to capture the
25560 number of frames requested by this callback.
25562 result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout);
25563 if (result != MA_SUCCESS) {
25564 #if defined(MA_DEBUG_OUTPUT)
25565 printf("Failed to allocate AudioBufferList for capture.");
25571 When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes
25572 that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer
25573 being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a
25574 problem when a future call to this callback specifies a larger number of frames.
25576 To work around this we need to explicitly set the size of each buffer to their respective size in bytes.
25578 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
25579 pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels;
25582 status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList);
25583 if (status != noErr) {
25584 #if defined(MA_DEBUG_OUTPUT)
25585 printf(" ERROR: AudioUnitRender() failed with %d\n", status);
25590 if (layout == ma_stream_layout_interleaved) {
25591 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
25592 if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) {
25593 ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount);
25595 #if defined(MA_DEBUG_OUTPUT)
25596 printf(" mDataByteSize=%d\n", pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
25600 This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
25601 not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams.
25603 ma_uint8 silentBuffer[4096];
25604 ma_uint32 framesRemaining;
25606 MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer));
25608 framesRemaining = frameCount;
25609 while (framesRemaining > 0) {
25610 ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
25611 if (framesToSend > framesRemaining) {
25612 framesToSend = framesRemaining;
25615 ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend);
25617 framesRemaining -= framesToSend;
25620 #if defined(MA_DEBUG_OUTPUT)
25621 printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
25626 /* 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. */
25627 MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */
25630 For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
25631 very strange has happened and we're not going to support it.
25633 if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) {
25634 ma_uint8 tempBuffer[4096];
25635 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) {
25636 ma_uint32 framesRemaining = frameCount;
25637 while (framesRemaining > 0) {
25638 void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
25639 ma_uint32 iChannel;
25640 ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
25641 if (framesToSend > framesRemaining) {
25642 framesToSend = framesRemaining;
25645 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
25646 ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
25649 ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);
25650 ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend);
25652 framesRemaining -= framesToSend;
25658 (void)pActionFlags;
25662 (void)pUnusedBufferList;
25667 static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element)
25669 ma_device* pDevice = (ma_device*)pUserData;
25670 MA_ASSERT(pDevice != NULL);
25673 There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like
25674 AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit)
25675 can try waiting on the same lock. I'm going to try working around this by not calling any Core
25676 Audio APIs in the callback when the device has been stopped or uninitialized.
25678 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) {
25679 ma_stop_proc onStop = pDevice->onStop;
25684 ma_event_signal(&pDevice->coreaudio.stopEvent);
25687 UInt32 isRunningSize = sizeof(isRunning);
25688 OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize);
25689 if (status != noErr) {
25690 return; /* Don't really know what to do in this case... just ignore it, I suppose... */
25694 ma_stop_proc onStop;
25697 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:
25699 1) When the device is unplugged, this will be called _before_ the default device change notification.
25700 2) When the device is changed via the default device change notification, this will be called _after_ the switch.
25702 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.
25704 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) ||
25705 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) {
25707 It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device
25708 via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the
25709 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
25712 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
25713 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
25718 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
25719 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
25720 likely be successful in switching to the new device.
25722 TODO: Try to predict if Core Audio will switch devices. If not, the onStop callback needs to be posted.
25727 /* Getting here means we need to stop the device. */
25728 onStop = pDevice->onStop;
25735 (void)propertyID; /* Unused. */
25738 #if defined(MA_APPLE_DESKTOP)
25739 static ma_spinlock g_DeviceTrackingInitLock_CoreAudio = 0; /* A spinlock for mutal exclusion of the init/uninit of the global tracking data. Initialization to 0 is what we need. */
25740 static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0;
25741 static ma_mutex g_DeviceTrackingMutex_CoreAudio;
25742 static ma_device** g_ppTrackedDevices_CoreAudio = NULL;
25743 static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0;
25744 static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0;
25746 static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData)
25748 ma_device_type deviceType;
25750 /* Not sure if I really need to check this, but it makes me feel better. */
25751 if (addressCount == 0) {
25755 if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) {
25756 deviceType = ma_device_type_playback;
25757 } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) {
25758 deviceType = ma_device_type_capture;
25760 return noErr; /* Should never hit this. */
25763 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
25766 for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
25767 ma_result reinitResult;
25768 ma_device* pDevice;
25770 pDevice = g_ppTrackedDevices_CoreAudio[iDevice];
25771 if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) {
25772 if (deviceType == ma_device_type_playback) {
25773 pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE;
25774 reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
25775 pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE;
25777 pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE;
25778 reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
25779 pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE;
25782 if (reinitResult == MA_SUCCESS) {
25783 ma_device__post_init_setup(pDevice, deviceType);
25785 /* Restart the device if required. If this fails we need to stop the device entirely. */
25786 if (ma_device_get_state(pDevice) == MA_STATE_STARTED) {
25788 if (deviceType == ma_device_type_playback) {
25789 status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
25790 if (status != noErr) {
25791 if (pDevice->type == ma_device_type_duplex) {
25792 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
25794 ma_device__set_state(pDevice, MA_STATE_STOPPED);
25796 } else if (deviceType == ma_device_type_capture) {
25797 status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
25798 if (status != noErr) {
25799 if (pDevice->type == ma_device_type_duplex) {
25800 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
25802 ma_device__set_state(pDevice, MA_STATE_STOPPED);
25810 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
25812 /* Unused parameters. */
25819 static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext)
25821 MA_ASSERT(pContext != NULL);
25823 ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);
25825 /* Don't do anything if we've already initializd device tracking. */
25826 if (g_DeviceTrackingInitCounter_CoreAudio == 0) {
25827 AudioObjectPropertyAddress propAddress;
25828 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
25829 propAddress.mElement = kAudioObjectPropertyElementMaster;
25831 ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio);
25833 propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
25834 ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
25836 propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
25837 ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
25839 g_DeviceTrackingInitCounter_CoreAudio += 1;
25842 ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
25847 static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext)
25849 MA_ASSERT(pContext != NULL);
25851 ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);
25853 g_DeviceTrackingInitCounter_CoreAudio -= 1;
25855 if (g_DeviceTrackingInitCounter_CoreAudio == 0) {
25856 AudioObjectPropertyAddress propAddress;
25857 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
25858 propAddress.mElement = kAudioObjectPropertyElementMaster;
25860 propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
25861 ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
25863 propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
25864 ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
25866 /* At this point there should be no tracked devices. If not there's an error somewhere. */
25867 if (g_ppTrackedDevices_CoreAudio != NULL) {
25868 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);
25871 ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio);
25874 ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
25879 static ma_result ma_device__track__coreaudio(ma_device* pDevice)
25881 MA_ASSERT(pDevice != NULL);
25883 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
25885 /* Allocate memory if required. */
25886 if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) {
25889 ma_device** ppNewDevices;
25891 oldCap = g_TrackedDeviceCap_CoreAudio;
25892 newCap = g_TrackedDeviceCap_CoreAudio * 2;
25897 ppNewDevices = (ma_device**)ma__realloc_from_callbacks(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, sizeof(*g_ppTrackedDevices_CoreAudio)*oldCap, &pDevice->pContext->allocationCallbacks);
25898 if (ppNewDevices == NULL) {
25899 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
25900 return MA_OUT_OF_MEMORY;
25903 g_ppTrackedDevices_CoreAudio = ppNewDevices;
25904 g_TrackedDeviceCap_CoreAudio = newCap;
25907 g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice;
25908 g_TrackedDeviceCount_CoreAudio += 1;
25910 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
25915 static ma_result ma_device__untrack__coreaudio(ma_device* pDevice)
25917 MA_ASSERT(pDevice != NULL);
25919 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
25922 for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
25923 if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) {
25924 /* We've found the device. We now need to remove it from the list. */
25926 for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) {
25927 g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1];
25930 g_TrackedDeviceCount_CoreAudio -= 1;
25932 /* If there's nothing else in the list we need to free memory. */
25933 if (g_TrackedDeviceCount_CoreAudio == 0) {
25934 ma__free_from_callbacks(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks);
25935 g_ppTrackedDevices_CoreAudio = NULL;
25936 g_TrackedDeviceCap_CoreAudio = 0;
25943 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
25949 #if defined(MA_APPLE_MOBILE)
25950 @interface ma_router_change_handler:NSObject {
25951 ma_device* m_pDevice;
25955 @implementation ma_router_change_handler
25956 -(id)init:(ma_device*)pDevice
25958 self = [super init];
25959 m_pDevice = pDevice;
25961 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
25968 [self remove_handler];
25971 -(void)remove_handler
25973 [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
25976 -(void)handle_route_change:(NSNotification*)pNotification
25978 AVAudioSession* pSession = [AVAudioSession sharedInstance];
25980 NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
25983 case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
25985 #if defined(MA_DEBUG_OUTPUT)
25986 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n");
25990 case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
25992 #if defined(MA_DEBUG_OUTPUT)
25993 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n");
25997 case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
25999 #if defined(MA_DEBUG_OUTPUT)
26000 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n");
26004 case AVAudioSessionRouteChangeReasonWakeFromSleep:
26006 #if defined(MA_DEBUG_OUTPUT)
26007 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n");
26011 case AVAudioSessionRouteChangeReasonOverride:
26013 #if defined(MA_DEBUG_OUTPUT)
26014 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n");
26018 case AVAudioSessionRouteChangeReasonCategoryChange:
26020 #if defined(MA_DEBUG_OUTPUT)
26021 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n");
26025 case AVAudioSessionRouteChangeReasonUnknown:
26028 #if defined(MA_DEBUG_OUTPUT)
26029 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n");
26034 #if defined(MA_DEBUG_OUTPUT)
26035 printf("[Core Audio] Changing Route. inputNumberChannels=%d; outputNumberOfChannels=%d\n", (int)pSession.inputNumberOfChannels, (int)pSession.outputNumberOfChannels);
26038 ma_uint32 previousState = ma_device_get_state(m_pDevice);
26040 if (previousState == MA_STATE_STARTED) {
26041 ma_device_stop(m_pDevice);
26044 if (m_pDevice->type == ma_device_type_capture || m_pDevice->type == ma_device_type_duplex) {
26045 m_pDevice->capture.internalChannels = (ma_uint32)pSession.inputNumberOfChannels;
26046 m_pDevice->capture.internalSampleRate = (ma_uint32)pSession.sampleRate;
26047 ma_device__post_init_setup(m_pDevice, ma_device_type_capture);
26049 if (m_pDevice->type == ma_device_type_playback || m_pDevice->type == ma_device_type_duplex) {
26050 m_pDevice->playback.internalChannels = (ma_uint32)pSession.outputNumberOfChannels;
26051 m_pDevice->playback.internalSampleRate = (ma_uint32)pSession.sampleRate;
26052 ma_device__post_init_setup(m_pDevice, ma_device_type_playback);
26055 if (previousState == MA_STATE_STARTED) {
26056 ma_device_start(m_pDevice);
26062 static ma_result ma_device_uninit__coreaudio(ma_device* pDevice)
26064 MA_ASSERT(pDevice != NULL);
26065 MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_UNINITIALIZED);
26067 #if defined(MA_APPLE_DESKTOP)
26069 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
26070 just gracefully ignore it.
26072 ma_device__untrack__coreaudio(pDevice);
26074 #if defined(MA_APPLE_MOBILE)
26075 if (pDevice->coreaudio.pRouteChangeHandler != NULL) {
26076 ma_router_change_handler* pRouteChangeHandler = (__bridge_transfer ma_router_change_handler*)pDevice->coreaudio.pRouteChangeHandler;
26077 [pRouteChangeHandler remove_handler];
26081 if (pDevice->coreaudio.audioUnitCapture != NULL) {
26082 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
26084 if (pDevice->coreaudio.audioUnitPlayback != NULL) {
26085 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
26088 if (pDevice->coreaudio.pAudioBufferList) {
26089 ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
26097 ma_bool32 allowNominalSampleRateChange;
26100 ma_format formatIn;
26101 ma_uint32 channelsIn;
26102 ma_uint32 sampleRateIn;
26103 ma_channel channelMapIn[MA_MAX_CHANNELS];
26104 ma_uint32 periodSizeInFramesIn;
26105 ma_uint32 periodSizeInMillisecondsIn;
26106 ma_uint32 periodsIn;
26107 ma_share_mode shareMode;
26108 ma_performance_profile performanceProfile;
26109 ma_bool32 registerStopEvent;
26112 #if defined(MA_APPLE_DESKTOP)
26113 AudioObjectID deviceObjectID;
26115 AudioComponent component;
26116 AudioUnit audioUnit;
26117 AudioBufferList* pAudioBufferList; /* Only used for input devices. */
26118 ma_format formatOut;
26119 ma_uint32 channelsOut;
26120 ma_uint32 sampleRateOut;
26121 ma_channel channelMapOut[MA_MAX_CHANNELS];
26122 ma_uint32 periodSizeInFramesOut;
26123 ma_uint32 periodsOut;
26124 char deviceName[256];
26125 } ma_device_init_internal_data__coreaudio;
26127 static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */
26131 UInt32 enableIOFlag;
26132 AudioStreamBasicDescription bestFormat;
26133 UInt32 actualPeriodSizeInFrames;
26134 AURenderCallbackStruct callbackInfo;
26135 #if defined(MA_APPLE_DESKTOP)
26136 AudioObjectID deviceObjectID;
26138 UInt32 actualPeriodSizeInFramesSize = sizeof(actualPeriodSizeInFrames);
26141 /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */
26142 if (deviceType == ma_device_type_duplex) {
26143 return MA_INVALID_ARGS;
26146 MA_ASSERT(pContext != NULL);
26147 MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture);
26149 #if defined(MA_APPLE_DESKTOP)
26150 pData->deviceObjectID = 0;
26152 pData->component = NULL;
26153 pData->audioUnit = NULL;
26154 pData->pAudioBufferList = NULL;
26156 #if defined(MA_APPLE_DESKTOP)
26157 result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
26158 if (result != MA_SUCCESS) {
26162 pData->deviceObjectID = deviceObjectID;
26165 /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */
26166 pData->periodsOut = pData->periodsIn;
26167 if (pData->periodsOut == 0) {
26168 pData->periodsOut = MA_DEFAULT_PERIODS;
26170 if (pData->periodsOut > 16) {
26171 pData->periodsOut = 16;
26176 status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit);
26177 if (status != noErr) {
26178 return ma_result_from_OSStatus(status);
26182 /* 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. */
26184 if (deviceType == ma_device_type_capture) {
26188 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
26189 if (status != noErr) {
26190 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26191 return ma_result_from_OSStatus(status);
26194 enableIOFlag = (enableIOFlag == 0) ? 1 : 0;
26195 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
26196 if (status != noErr) {
26197 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26198 return ma_result_from_OSStatus(status);
26202 /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */
26203 #if defined(MA_APPLE_DESKTOP)
26204 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID));
26205 if (status != noErr) {
26206 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26207 return ma_result_from_OSStatus(result);
26211 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
26212 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.
26214 if (pDeviceID != NULL) {
26215 if (deviceType == ma_device_type_capture) {
26216 ma_bool32 found = MA_FALSE;
26217 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
26218 for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
26219 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
26220 [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil];
26226 if (found == MA_FALSE) {
26227 return MA_DOES_NOT_EXIST;
26234 Format. This is the hardest part of initialization because there's a few variables to take into account.
26235 1) The format must be supported by the device.
26236 2) The format must be supported miniaudio.
26237 3) There's a priority that miniaudio prefers.
26239 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
26240 most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same
26241 for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely.
26243 On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to.
26246 AudioStreamBasicDescription origFormat;
26247 UInt32 origFormatSize = sizeof(origFormat);
26248 AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
26249 AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
26251 if (deviceType == ma_device_type_playback) {
26252 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize);
26254 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize);
26256 if (status != noErr) {
26257 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26258 return ma_result_from_OSStatus(status);
26261 #if defined(MA_APPLE_DESKTOP)
26262 result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat);
26263 if (result != MA_SUCCESS) {
26264 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26269 Technical Note TN2091: Device input using the HAL Output Audio Unit
26270 https://developer.apple.com/library/archive/technotes/tn2091/_index.html
26272 This documentation says the following:
26274 The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY
26275 variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate
26276 conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with
26277 another AudioConverter.
26279 The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We
26280 therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it
26281 safe and apply the same rule to output as well.
26283 I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender()
26284 returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but
26285 this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format.
26287 Something that does seem to work, however, has been setting the nominal sample rate on the deivce object. The problem with
26288 this, however, is that it actually changes the sample rate at the operating system level and not just the application. This
26289 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
26290 configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample
26291 rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run
26292 the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is
26293 changed by miniaudio.
26295 if (pData->allowNominalSampleRateChange) {
26296 AudioValueRange sampleRateRange;
26297 AudioObjectPropertyAddress propAddress;
26299 sampleRateRange.mMinimum = bestFormat.mSampleRate;
26300 sampleRateRange.mMaximum = bestFormat.mSampleRate;
26302 propAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
26303 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
26304 propAddress.mElement = kAudioObjectPropertyElementMaster;
26306 status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange);
26307 if (status != noErr) {
26308 bestFormat.mSampleRate = origFormat.mSampleRate;
26311 bestFormat.mSampleRate = origFormat.mSampleRate;
26314 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
26315 if (status != noErr) {
26316 /* We failed to set the format, so fall back to the current format of the audio unit. */
26317 bestFormat = origFormat;
26320 bestFormat = origFormat;
26323 Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try
26324 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
26325 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
26326 can tell, it looks like the sample rate is shared between playback and capture for everything.
26329 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
26330 MA_ASSERT(pAudioSession != NULL);
26332 [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil];
26333 bestFormat.mSampleRate = pAudioSession.sampleRate;
26336 I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with
26337 AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead.
26339 if (deviceType == ma_device_type_playback) {
26340 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels;
26342 if (deviceType == ma_device_type_capture) {
26343 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels;
26347 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
26348 if (status != noErr) {
26349 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26350 return ma_result_from_OSStatus(status);
26354 result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut);
26355 if (result != MA_SUCCESS) {
26356 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26360 if (pData->formatOut == ma_format_unknown) {
26361 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26362 return MA_FORMAT_NOT_SUPPORTED;
26365 pData->channelsOut = bestFormat.mChannelsPerFrame;
26366 pData->sampleRateOut = bestFormat.mSampleRate;
26369 /* Clamp the channel count for safety. */
26370 if (pData->channelsOut > MA_MAX_CHANNELS) {
26371 pData->channelsOut = MA_MAX_CHANNELS;
26375 Internal channel map. This is weird in my testing. If I use the AudioObject to get the
26376 channel map, the channel descriptions are set to "Unknown" for some reason. To work around
26377 this it looks like retrieving it from the AudioUnit will work. However, and this is where
26378 it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore
26379 I'm going to fall back to a default assumption in these cases.
26381 #if defined(MA_APPLE_DESKTOP)
26382 result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut);
26383 if (result != MA_SUCCESS) {
26385 /* Try falling back to the channel map from the AudioObject. */
26386 result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut);
26387 if (result != MA_SUCCESS) {
26391 /* Fall back to default assumptions. */
26392 ma_get_standard_channel_map(ma_standard_channel_map_default, pData->channelsOut, pData->channelMapOut);
26396 /* TODO: Figure out how to get the channel map using AVAudioSession. */
26397 ma_get_standard_channel_map(ma_standard_channel_map_default, pData->channelsOut, pData->channelMapOut);
26401 /* Buffer size. Not allowing this to be configurable on iOS. */
26402 if (pData->periodSizeInFramesIn == 0) {
26403 if (pData->periodSizeInMillisecondsIn == 0) {
26404 if (pData->performanceProfile == ma_performance_profile_low_latency) {
26405 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pData->sampleRateOut);
26407 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pData->sampleRateOut);
26410 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut);
26413 actualPeriodSizeInFrames = pData->periodSizeInFramesIn;
26416 #if defined(MA_APPLE_DESKTOP)
26417 result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames);
26418 if (result != MA_SUCCESS) {
26423 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
26424 just going to set it to the value of kAudioUnitProperty_MaximumFramesPerSlice.
26426 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, &actualPeriodSizeInFramesSize);
26427 if (status != noErr) {
26428 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26429 return ma_result_from_OSStatus(status);
26435 During testing I discovered that the buffer size can be too big. You'll get an error like this:
26437 kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512
26439 Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that
26440 of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice.
26442 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames));
26443 if (status != noErr) {
26444 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26445 return ma_result_from_OSStatus(status);
26448 pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames;
26450 /* We need a buffer list if this is an input device. We render into this in the input callback. */
26451 if (deviceType == ma_device_type_capture) {
26452 ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
26453 AudioBufferList* pBufferList;
26455 pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks);
26456 if (pBufferList == NULL) {
26457 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26458 return MA_OUT_OF_MEMORY;
26461 pData->pAudioBufferList = pBufferList;
26465 callbackInfo.inputProcRefCon = pDevice_DoNotReference;
26466 if (deviceType == ma_device_type_playback) {
26467 callbackInfo.inputProc = ma_on_output__coreaudio;
26468 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));
26469 if (status != noErr) {
26470 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26471 return ma_result_from_OSStatus(status);
26474 callbackInfo.inputProc = ma_on_input__coreaudio;
26475 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));
26476 if (status != noErr) {
26477 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26478 return ma_result_from_OSStatus(status);
26482 /* We need to listen for stop events. */
26483 if (pData->registerStopEvent) {
26484 status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference);
26485 if (status != noErr) {
26486 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26487 return ma_result_from_OSStatus(status);
26491 /* Initialize the audio unit. */
26492 status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit);
26493 if (status != noErr) {
26494 ma__free_from_callbacks(pData->pAudioBufferList, &pContext->allocationCallbacks);
26495 pData->pAudioBufferList = NULL;
26496 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26497 return ma_result_from_OSStatus(status);
26500 /* Grab the name. */
26501 #if defined(MA_APPLE_DESKTOP)
26502 ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName);
26504 if (deviceType == ma_device_type_playback) {
26505 ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
26507 ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME);
26514 #if defined(MA_APPLE_DESKTOP)
26515 static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit)
26517 ma_device_init_internal_data__coreaudio data;
26520 /* This should only be called for playback or capture, not duplex. */
26521 if (deviceType == ma_device_type_duplex) {
26522 return MA_INVALID_ARGS;
26525 data.allowNominalSampleRateChange = MA_FALSE; /* Don't change the nominal sample rate when switching devices. */
26527 if (deviceType == ma_device_type_capture) {
26528 data.formatIn = pDevice->capture.format;
26529 data.channelsIn = pDevice->capture.channels;
26530 data.sampleRateIn = pDevice->sampleRate;
26531 MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
26532 data.shareMode = pDevice->capture.shareMode;
26533 data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile;
26534 data.registerStopEvent = MA_TRUE;
26536 if (disposePreviousAudioUnit) {
26537 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
26538 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
26540 if (pDevice->coreaudio.pAudioBufferList) {
26541 ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
26543 } else if (deviceType == ma_device_type_playback) {
26544 data.formatIn = pDevice->playback.format;
26545 data.channelsIn = pDevice->playback.channels;
26546 data.sampleRateIn = pDevice->sampleRate;
26547 MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
26548 data.shareMode = pDevice->playback.shareMode;
26549 data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile;
26550 data.registerStopEvent = (pDevice->type != ma_device_type_duplex);
26552 if (disposePreviousAudioUnit) {
26553 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
26554 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
26557 data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames;
26558 data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds;
26559 data.periodsIn = pDevice->coreaudio.originalPeriods;
26561 /* Need at least 3 periods for duplex. */
26562 if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) {
26563 data.periodsIn = 3;
26566 result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice);
26567 if (result != MA_SUCCESS) {
26571 if (deviceType == ma_device_type_capture) {
26572 #if defined(MA_APPLE_DESKTOP)
26573 pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
26575 pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
26576 pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
26577 pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut;
26579 pDevice->capture.internalFormat = data.formatOut;
26580 pDevice->capture.internalChannels = data.channelsOut;
26581 pDevice->capture.internalSampleRate = data.sampleRateOut;
26582 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
26583 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
26584 pDevice->capture.internalPeriods = data.periodsOut;
26585 } else if (deviceType == ma_device_type_playback) {
26586 #if defined(MA_APPLE_DESKTOP)
26587 pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
26589 pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
26591 pDevice->playback.internalFormat = data.formatOut;
26592 pDevice->playback.internalChannels = data.channelsOut;
26593 pDevice->playback.internalSampleRate = data.sampleRateOut;
26594 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
26595 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
26596 pDevice->playback.internalPeriods = data.periodsOut;
26601 #endif /* MA_APPLE_DESKTOP */
26603 static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
26607 MA_ASSERT(pDevice != NULL);
26608 MA_ASSERT(pConfig != NULL);
26610 if (pConfig->deviceType == ma_device_type_loopback) {
26611 return MA_DEVICE_TYPE_NOT_SUPPORTED;
26614 /* No exclusive mode with the Core Audio backend for now. */
26615 if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) ||
26616 ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) {
26617 return MA_SHARE_MODE_NOT_SUPPORTED;
26620 /* Capture needs to be initialized first. */
26621 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
26622 ma_device_init_internal_data__coreaudio data;
26623 data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange;
26624 data.formatIn = pDescriptorCapture->format;
26625 data.channelsIn = pDescriptorCapture->channels;
26626 data.sampleRateIn = pDescriptorCapture->sampleRate;
26627 MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
26628 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
26629 data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds;
26630 data.periodsIn = pDescriptorCapture->periodCount;
26631 data.shareMode = pDescriptorCapture->shareMode;
26632 data.performanceProfile = pConfig->performanceProfile;
26633 data.registerStopEvent = MA_TRUE;
26635 /* Need at least 3 periods for duplex. */
26636 if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) {
26637 data.periodsIn = 3;
26640 result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice);
26641 if (result != MA_SUCCESS) {
26645 pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL);
26646 #if defined(MA_APPLE_DESKTOP)
26647 pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
26649 pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
26650 pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
26651 pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut;
26652 pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
26653 pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;
26654 pDevice->coreaudio.originalPeriods = pDescriptorCapture->periodCount;
26655 pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile;
26657 pDescriptorCapture->format = data.formatOut;
26658 pDescriptorCapture->channels = data.channelsOut;
26659 pDescriptorCapture->sampleRate = data.sampleRateOut;
26660 MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
26661 pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut;
26662 pDescriptorCapture->periodCount = data.periodsOut;
26664 #if defined(MA_APPLE_DESKTOP)
26666 If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
26667 switch the device in the background.
26669 if (pConfig->capture.pDeviceID == NULL) {
26670 ma_device__track__coreaudio(pDevice);
26676 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
26677 ma_device_init_internal_data__coreaudio data;
26678 data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange;
26679 data.formatIn = pDescriptorPlayback->format;
26680 data.channelsIn = pDescriptorPlayback->channels;
26681 data.sampleRateIn = pDescriptorPlayback->sampleRate;
26682 MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
26683 data.shareMode = pDescriptorPlayback->shareMode;
26684 data.performanceProfile = pConfig->performanceProfile;
26686 /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */
26687 if (pConfig->deviceType == ma_device_type_duplex) {
26688 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
26689 data.periodsIn = pDescriptorCapture->periodCount;
26690 data.registerStopEvent = MA_FALSE;
26692 data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames;
26693 data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;
26694 data.periodsIn = pDescriptorPlayback->periodCount;
26695 data.registerStopEvent = MA_TRUE;
26698 result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice);
26699 if (result != MA_SUCCESS) {
26700 if (pConfig->deviceType == ma_device_type_duplex) {
26701 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
26702 if (pDevice->coreaudio.pAudioBufferList) {
26703 ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
26709 pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL);
26710 #if defined(MA_APPLE_DESKTOP)
26711 pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
26713 pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
26714 pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
26715 pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;
26716 pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount;
26717 pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile;
26719 pDescriptorPlayback->format = data.formatOut;
26720 pDescriptorPlayback->channels = data.channelsOut;
26721 pDescriptorPlayback->sampleRate = data.sampleRateOut;
26722 MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
26723 pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut;
26724 pDescriptorPlayback->periodCount = data.periodsOut;
26726 #if defined(MA_APPLE_DESKTOP)
26728 If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
26729 switch the device in the background.
26731 if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) {
26732 ma_device__track__coreaudio(pDevice);
26740 When stopping the device, a callback is called on another thread. We need to wait for this callback
26741 before returning from ma_device_stop(). This event is used for this.
26743 ma_event_init(&pDevice->coreaudio.stopEvent);
26746 We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done
26747 differently on non-Desktop Apple platforms.
26749 #if defined(MA_APPLE_MOBILE)
26750 pDevice->coreaudio.pRouteChangeHandler = (__bridge_retained void*)[[ma_router_change_handler alloc] init:pDevice];
26757 static ma_result ma_device_start__coreaudio(ma_device* pDevice)
26759 MA_ASSERT(pDevice != NULL);
26761 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26762 OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
26763 if (status != noErr) {
26764 return ma_result_from_OSStatus(status);
26768 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26769 OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
26770 if (status != noErr) {
26771 if (pDevice->type == ma_device_type_duplex) {
26772 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
26774 return ma_result_from_OSStatus(status);
26781 static ma_result ma_device_stop__coreaudio(ma_device* pDevice)
26783 MA_ASSERT(pDevice != NULL);
26785 /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */
26787 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26788 OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
26789 if (status != noErr) {
26790 return ma_result_from_OSStatus(status);
26794 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26795 OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
26796 if (status != noErr) {
26797 return ma_result_from_OSStatus(status);
26801 /* We need to wait for the callback to finish before returning. */
26802 ma_event_wait(&pDevice->coreaudio.stopEvent);
26807 static ma_result ma_context_uninit__coreaudio(ma_context* pContext)
26809 MA_ASSERT(pContext != NULL);
26810 MA_ASSERT(pContext->backend == ma_backend_coreaudio);
26812 #if defined(MA_APPLE_MOBILE)
26813 if (!pContext->coreaudio.noAudioSessionDeactivate) {
26814 if (![[AVAudioSession sharedInstance] setActive:false error:nil]) {
26815 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session.", MA_FAILED_TO_INIT_BACKEND);
26820 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
26821 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
26822 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
26823 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
26826 #if !defined(MA_APPLE_MOBILE)
26827 ma_context__uninit_device_tracking__coreaudio(pContext);
26834 #if defined(MA_APPLE_MOBILE)
26835 static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category)
26837 /* The "default" and "none" categories are treated different and should not be used as an input into this function. */
26838 MA_ASSERT(category != ma_ios_session_category_default);
26839 MA_ASSERT(category != ma_ios_session_category_none);
26841 switch (category) {
26842 case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient;
26843 case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient;
26844 case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback;
26845 case ma_ios_session_category_record: return AVAudioSessionCategoryRecord;
26846 case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord;
26847 case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute;
26848 case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient;
26849 case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient;
26850 default: return AVAudioSessionCategoryAmbient;
26855 static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
26857 #if !defined(MA_APPLE_MOBILE)
26861 MA_ASSERT(pConfig != NULL);
26862 MA_ASSERT(pContext != NULL);
26864 #if defined(MA_APPLE_MOBILE)
26866 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
26867 AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions;
26869 MA_ASSERT(pAudioSession != NULL);
26871 if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) {
26873 I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails
26874 we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category.
26876 #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH)
26877 options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
26880 if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) {
26881 /* Using PlayAndRecord */
26882 } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) {
26883 /* Using Playback */
26884 } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) {
26887 /* Leave as default? */
26890 if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) {
26891 if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) {
26892 return MA_INVALID_OPERATION; /* Failed to set session category. */
26897 if (!pConfig->coreaudio.noAudioSessionActivate) {
26898 if (![pAudioSession setActive:true error:nil]) {
26899 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to activate audio session.", MA_FAILED_TO_INIT_BACKEND);
26905 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
26906 pContext->coreaudio.hCoreFoundation = ma_dlopen(pContext, "CoreFoundation.framework/CoreFoundation");
26907 if (pContext->coreaudio.hCoreFoundation == NULL) {
26908 return MA_API_NOT_FOUND;
26911 pContext->coreaudio.CFStringGetCString = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFStringGetCString");
26912 pContext->coreaudio.CFRelease = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFRelease");
26915 pContext->coreaudio.hCoreAudio = ma_dlopen(pContext, "CoreAudio.framework/CoreAudio");
26916 if (pContext->coreaudio.hCoreAudio == NULL) {
26917 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
26918 return MA_API_NOT_FOUND;
26921 pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData");
26922 pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize");
26923 pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData");
26924 pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener");
26925 pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener");
26928 It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still
26929 defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback.
26930 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
26933 pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioUnit.framework/AudioUnit");
26934 if (pContext->coreaudio.hAudioUnit == NULL) {
26935 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
26936 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
26937 return MA_API_NOT_FOUND;
26940 if (ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) {
26941 /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */
26942 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
26943 pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioToolbox.framework/AudioToolbox");
26944 if (pContext->coreaudio.hAudioUnit == NULL) {
26945 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
26946 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
26947 return MA_API_NOT_FOUND;
26951 pContext->coreaudio.AudioComponentFindNext = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext");
26952 pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose");
26953 pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew");
26954 pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart");
26955 pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop");
26956 pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener");
26957 pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo");
26958 pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty");
26959 pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty");
26960 pContext->coreaudio.AudioUnitInitialize = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitInitialize");
26961 pContext->coreaudio.AudioUnitRender = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitRender");
26963 pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString;
26964 pContext->coreaudio.CFRelease = (ma_proc)CFRelease;
26966 #if defined(MA_APPLE_DESKTOP)
26967 pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData;
26968 pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize;
26969 pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData;
26970 pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener;
26971 pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener;
26974 pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext;
26975 pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose;
26976 pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew;
26977 pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart;
26978 pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop;
26979 pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener;
26980 pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo;
26981 pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty;
26982 pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty;
26983 pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize;
26984 pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender;
26987 /* Audio component. */
26989 AudioComponentDescription desc;
26990 desc.componentType = kAudioUnitType_Output;
26991 #if defined(MA_APPLE_DESKTOP)
26992 desc.componentSubType = kAudioUnitSubType_HALOutput;
26994 desc.componentSubType = kAudioUnitSubType_RemoteIO;
26996 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
26997 desc.componentFlags = 0;
26998 desc.componentFlagsMask = 0;
27000 pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
27001 if (pContext->coreaudio.component == NULL) {
27002 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
27003 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
27004 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
27005 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
27007 return MA_FAILED_TO_INIT_BACKEND;
27011 #if !defined(MA_APPLE_MOBILE)
27012 result = ma_context__init_device_tracking__coreaudio(pContext);
27013 if (result != MA_SUCCESS) {
27014 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
27015 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
27016 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
27017 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
27023 pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate;
27025 pCallbacks->onContextInit = ma_context_init__coreaudio;
27026 pCallbacks->onContextUninit = ma_context_uninit__coreaudio;
27027 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio;
27028 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__coreaudio;
27029 pCallbacks->onDeviceInit = ma_device_init__coreaudio;
27030 pCallbacks->onDeviceUninit = ma_device_uninit__coreaudio;
27031 pCallbacks->onDeviceStart = ma_device_start__coreaudio;
27032 pCallbacks->onDeviceStop = ma_device_stop__coreaudio;
27033 pCallbacks->onDeviceRead = NULL;
27034 pCallbacks->onDeviceWrite = NULL;
27035 pCallbacks->onDeviceDataLoop = NULL;
27039 #endif /* Core Audio */
27043 /******************************************************************************
27047 ******************************************************************************/
27048 #ifdef MA_HAS_SNDIO
27052 Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due
27053 to miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device
27054 just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's
27055 demand for it or if I can get it tested and debugged more thoroughly.
27058 #if defined(__NetBSD__) || defined(__OpenBSD__)
27059 #include <sys/audioio.h>
27061 #if defined(__FreeBSD__) || defined(__DragonFly__)
27062 #include <sys/soundcard.h>
27066 #define MA_SIO_DEVANY "default"
27067 #define MA_SIO_PLAY 1
27068 #define MA_SIO_REC 2
27069 #define MA_SIO_NENC 8
27070 #define MA_SIO_NCHAN 8
27071 #define MA_SIO_NRATE 16
27072 #define MA_SIO_NCONF 4
27074 struct ma_sio_hdl; /* <-- Opaque */
27083 unsigned int rchan;
27084 unsigned int pchan;
27086 unsigned int bufsz;
27088 unsigned int round;
27089 unsigned int appbufsz;
27091 unsigned int __magic;
27106 unsigned int rchan;
27107 unsigned int pchan;
27113 struct ma_sio_enc enc[MA_SIO_NENC];
27114 unsigned int rchan[MA_SIO_NCHAN];
27115 unsigned int pchan[MA_SIO_NCHAN];
27116 unsigned int rate[MA_SIO_NRATE];
27118 unsigned int nconf;
27119 struct ma_sio_conf confs[MA_SIO_NCONF];
27122 typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int);
27123 typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*);
27124 typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
27125 typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
27126 typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*);
27127 typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t);
27128 typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t);
27129 typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*);
27130 typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*);
27131 typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*);
27133 static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */
27136 for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) {
27137 if (g_maStandardSampleRatePriorities[i] == sampleRate) {
27142 return (ma_uint32)-1;
27145 static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb)
27147 /* We only support native-endian right now. */
27148 if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) {
27149 return ma_format_unknown;
27152 if (bits == 8 && bps == 1 && sig == 0) {
27153 return ma_format_u8;
27155 if (bits == 16 && bps == 2 && sig == 1) {
27156 return ma_format_s16;
27158 if (bits == 24 && bps == 3 && sig == 1) {
27159 return ma_format_s24;
27161 if (bits == 24 && bps == 4 && sig == 1 && msb == 0) {
27162 /*return ma_format_s24_32;*/
27164 if (bits == 32 && bps == 4 && sig == 1) {
27165 return ma_format_s32;
27168 return ma_format_unknown;
27171 static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps)
27173 ma_format bestFormat;
27174 unsigned int iConfig;
27176 MA_ASSERT(caps != NULL);
27178 bestFormat = ma_format_unknown;
27179 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
27180 unsigned int iEncoding;
27181 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
27189 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
27193 bits = caps->enc[iEncoding].bits;
27194 bps = caps->enc[iEncoding].bps;
27195 sig = caps->enc[iEncoding].sig;
27196 le = caps->enc[iEncoding].le;
27197 msb = caps->enc[iEncoding].msb;
27198 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
27199 if (format == ma_format_unknown) {
27200 continue; /* Format not supported. */
27203 if (bestFormat == ma_format_unknown) {
27204 bestFormat = format;
27206 if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */
27207 bestFormat = format;
27216 static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat)
27218 ma_uint32 maxChannels;
27219 unsigned int iConfig;
27221 MA_ASSERT(caps != NULL);
27222 MA_ASSERT(requiredFormat != ma_format_unknown);
27224 /* Just pick whatever configuration has the most channels. */
27226 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
27227 /* The encoding should be of requiredFormat. */
27228 unsigned int iEncoding;
27229 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
27230 unsigned int iChannel;
27238 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
27242 bits = caps->enc[iEncoding].bits;
27243 bps = caps->enc[iEncoding].bps;
27244 sig = caps->enc[iEncoding].sig;
27245 le = caps->enc[iEncoding].le;
27246 msb = caps->enc[iEncoding].msb;
27247 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
27248 if (format != requiredFormat) {
27252 /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
27253 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
27254 unsigned int chan = 0;
27255 unsigned int channels;
27257 if (deviceType == ma_device_type_playback) {
27258 chan = caps->confs[iConfig].pchan;
27260 chan = caps->confs[iConfig].rchan;
27263 if ((chan & (1UL << iChannel)) == 0) {
27267 if (deviceType == ma_device_type_playback) {
27268 channels = caps->pchan[iChannel];
27270 channels = caps->rchan[iChannel];
27273 if (maxChannels < channels) {
27274 maxChannels = channels;
27280 return maxChannels;
27283 static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat, ma_uint32 requiredChannels)
27285 ma_uint32 firstSampleRate;
27286 ma_uint32 bestSampleRate;
27287 unsigned int iConfig;
27289 MA_ASSERT(caps != NULL);
27290 MA_ASSERT(requiredFormat != ma_format_unknown);
27291 MA_ASSERT(requiredChannels > 0);
27292 MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS);
27294 firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */
27295 bestSampleRate = 0;
27297 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
27298 /* The encoding should be of requiredFormat. */
27299 unsigned int iEncoding;
27300 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
27301 unsigned int iChannel;
27309 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
27313 bits = caps->enc[iEncoding].bits;
27314 bps = caps->enc[iEncoding].bps;
27315 sig = caps->enc[iEncoding].sig;
27316 le = caps->enc[iEncoding].le;
27317 msb = caps->enc[iEncoding].msb;
27318 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
27319 if (format != requiredFormat) {
27323 /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
27324 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
27325 unsigned int chan = 0;
27326 unsigned int channels;
27327 unsigned int iRate;
27329 if (deviceType == ma_device_type_playback) {
27330 chan = caps->confs[iConfig].pchan;
27332 chan = caps->confs[iConfig].rchan;
27335 if ((chan & (1UL << iChannel)) == 0) {
27339 if (deviceType == ma_device_type_playback) {
27340 channels = caps->pchan[iChannel];
27342 channels = caps->rchan[iChannel];
27345 if (channels != requiredChannels) {
27349 /* Getting here means we have found a compatible encoding/channel pair. */
27350 for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
27351 ma_uint32 rate = (ma_uint32)caps->rate[iRate];
27352 ma_uint32 ratePriority;
27354 if (firstSampleRate == 0) {
27355 firstSampleRate = rate;
27358 /* Disregard this rate if it's not a standard one. */
27359 ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate);
27360 if (ratePriority == (ma_uint32)-1) {
27364 if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) { /* Lower = better. */
27365 bestSampleRate = rate;
27372 /* If a standard sample rate was not found just fall back to the first one that was iterated. */
27373 if (bestSampleRate == 0) {
27374 bestSampleRate = firstSampleRate;
27377 return bestSampleRate;
27381 static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
27383 ma_bool32 isTerminating = MA_FALSE;
27384 struct ma_sio_hdl* handle;
27386 MA_ASSERT(pContext != NULL);
27387 MA_ASSERT(callback != NULL);
27389 /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */
27392 if (!isTerminating) {
27393 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0);
27394 if (handle != NULL) {
27395 /* Supports playback. */
27396 ma_device_info deviceInfo;
27397 MA_ZERO_OBJECT(&deviceInfo);
27398 ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY);
27399 ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
27401 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
27403 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
27408 if (!isTerminating) {
27409 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0);
27410 if (handle != NULL) {
27411 /* Supports capture. */
27412 ma_device_info deviceInfo;
27413 MA_ZERO_OBJECT(&deviceInfo);
27414 ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default");
27415 ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME);
27417 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
27419 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
27426 static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
27429 struct ma_sio_hdl* handle;
27430 struct ma_sio_cap caps;
27431 unsigned int iConfig;
27433 MA_ASSERT(pContext != NULL);
27435 /* We need to open the device before we can get information about it. */
27436 if (pDeviceID == NULL) {
27437 ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY);
27438 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME);
27440 ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio);
27441 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid);
27444 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0);
27445 if (handle == NULL) {
27446 return MA_NO_DEVICE;
27449 if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) {
27453 pDeviceInfo->nativeDataFormatCount = 0;
27455 for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) {
27457 The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give
27458 preference to some formats over others.
27460 unsigned int iEncoding;
27461 unsigned int iChannel;
27462 unsigned int iRate;
27464 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
27472 if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) {
27476 bits = caps.enc[iEncoding].bits;
27477 bps = caps.enc[iEncoding].bps;
27478 sig = caps.enc[iEncoding].sig;
27479 le = caps.enc[iEncoding].le;
27480 msb = caps.enc[iEncoding].msb;
27481 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
27482 if (format == ma_format_unknown) {
27483 continue; /* Format not supported. */
27488 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
27489 unsigned int chan = 0;
27490 unsigned int channels;
27492 if (deviceType == ma_device_type_playback) {
27493 chan = caps.confs[iConfig].pchan;
27495 chan = caps.confs[iConfig].rchan;
27498 if ((chan & (1UL << iChannel)) == 0) {
27502 if (deviceType == ma_device_type_playback) {
27503 channels = caps.pchan[iChannel];
27505 channels = caps.rchan[iChannel];
27509 /* Sample Rates. */
27510 for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
27511 if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) {
27512 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, caps.rate[iRate], 0);
27519 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
27523 static ma_result ma_device_uninit__sndio(ma_device* pDevice)
27525 MA_ASSERT(pDevice != NULL);
27527 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27528 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
27531 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27532 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
27538 static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
27540 const char* pDeviceName;
27543 struct ma_sio_cap caps;
27544 struct ma_sio_par par;
27545 const ma_device_id* pDeviceID;
27547 ma_uint32 channels;
27548 ma_uint32 sampleRate;
27549 ma_format internalFormat;
27550 ma_uint32 internalChannels;
27551 ma_uint32 internalSampleRate;
27552 ma_uint32 internalPeriodSizeInFrames;
27553 ma_uint32 internalPeriods;
27555 MA_ASSERT(pConfig != NULL);
27556 MA_ASSERT(deviceType != ma_device_type_duplex);
27557 MA_ASSERT(pDevice != NULL);
27559 if (deviceType == ma_device_type_capture) {
27560 openFlags = MA_SIO_REC;
27562 openFlags = MA_SIO_PLAY;
27565 pDeviceID = pDescriptor->pDeviceID;
27566 format = pDescriptor->format;
27567 channels = pDescriptor->channels;
27568 sampleRate = pDescriptor->sampleRate;
27570 pDeviceName = MA_SIO_DEVANY;
27571 if (pDeviceID != NULL) {
27572 pDeviceName = pDeviceID->sndio;
27575 handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0);
27576 if (handle == NULL) {
27577 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
27580 /* We need to retrieve the device caps to determine the most appropriate format to use. */
27581 if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) {
27582 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
27583 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps.", MA_ERROR);
27587 Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real
27588 way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this
27589 to the requested channels, regardless of whether or not the default channel count is requested.
27591 For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the
27592 value returned by ma_find_best_channels_from_sio_cap__sndio().
27594 if (deviceType == ma_device_type_capture) {
27595 if (format == ma_format_unknown) {
27596 format = ma_find_best_format_from_sio_cap__sndio(&caps);
27599 if (channels == 0) {
27600 if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
27601 channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
27603 channels = MA_DEFAULT_CHANNELS;
27607 if (format == ma_format_unknown) {
27608 format = ma_find_best_format_from_sio_cap__sndio(&caps);
27611 if (channels == 0) {
27612 if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
27613 channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
27615 channels = MA_DEFAULT_CHANNELS;
27620 if (sampleRate == 0) {
27621 sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels);
27625 ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par);
27627 par.le = ma_is_little_endian();
27637 case ma_format_s24:
27644 case ma_format_s32:
27651 case ma_format_s16:
27652 case ma_format_f32:
27653 case ma_format_unknown:
27662 if (deviceType == ma_device_type_capture) {
27663 par.rchan = channels;
27665 par.pchan = channels;
27668 par.rate = sampleRate;
27670 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile);
27672 par.round = internalPeriodSizeInFrames;
27673 par.appbufsz = par.round * pDescriptor->periodCount;
27675 if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) {
27676 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
27677 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size.", MA_FORMAT_NOT_SUPPORTED);
27680 if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) {
27681 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
27682 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size.", MA_FORMAT_NOT_SUPPORTED);
27685 internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb);
27686 internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan;
27687 internalSampleRate = par.rate;
27688 internalPeriods = par.appbufsz / par.round;
27689 internalPeriodSizeInFrames = par.round;
27691 if (deviceType == ma_device_type_capture) {
27692 pDevice->sndio.handleCapture = handle;
27694 pDevice->sndio.handlePlayback = handle;
27697 pDescriptor->format = internalFormat;
27698 pDescriptor->channels = internalChannels;
27699 pDescriptor->sampleRate = internalSampleRate;
27700 ma_get_standard_channel_map(ma_standard_channel_map_sndio, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
27701 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
27702 pDescriptor->periodCount = internalPeriods;
27704 #ifdef MA_DEBUG_OUTPUT
27705 printf("DEVICE INFO\n");
27706 printf(" Format: %s\n", ma_get_format_name(internalFormat));
27707 printf(" Channels: %d\n", internalChannels);
27708 printf(" Sample Rate: %d\n", internalSampleRate);
27709 printf(" Period Size: %d\n", internalPeriodSizeInFrames);
27710 printf(" Periods: %d\n", internalPeriods);
27711 printf(" appbufsz: %d\n", par.appbufsz);
27712 printf(" round: %d\n", par.round);
27718 static ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
27720 MA_ASSERT(pDevice != NULL);
27722 MA_ZERO_OBJECT(&pDevice->sndio);
27724 if (pConfig->deviceType == ma_device_type_loopback) {
27725 return MA_DEVICE_TYPE_NOT_SUPPORTED;
27728 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
27729 ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
27730 if (result != MA_SUCCESS) {
27735 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
27736 ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
27737 if (result != MA_SUCCESS) {
27745 static ma_result ma_device_start__sndio(ma_device* pDevice)
27747 MA_ASSERT(pDevice != NULL);
27749 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27750 ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
27753 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
27754 ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */
27760 static ma_result ma_device_stop__sndio(ma_device* pDevice)
27762 MA_ASSERT(pDevice != NULL);
27765 From the documentation:
27767 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
27768 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
27769 buffer is drained. In no case are samples in the play buffer discarded.
27771 Therefore, sio_stop() performs all of the necessary draining for us.
27774 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27775 ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
27778 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
27779 ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
27785 static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
27789 if (pFramesWritten != NULL) {
27790 *pFramesWritten = 0;
27793 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));
27795 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device.", MA_IO_ERROR);
27798 if (pFramesWritten != NULL) {
27799 *pFramesWritten = frameCount;
27805 static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
27809 if (pFramesRead != NULL) {
27813 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));
27815 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);
27818 if (pFramesRead != NULL) {
27819 *pFramesRead = frameCount;
27825 static ma_result ma_context_uninit__sndio(ma_context* pContext)
27827 MA_ASSERT(pContext != NULL);
27828 MA_ASSERT(pContext->backend == ma_backend_sndio);
27834 static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
27836 #ifndef MA_NO_RUNTIME_LINKING
27837 const char* libsndioNames[] = {
27842 for (i = 0; i < ma_countof(libsndioNames); ++i) {
27843 pContext->sndio.sndioSO = ma_dlopen(pContext, libsndioNames[i]);
27844 if (pContext->sndio.sndioSO != NULL) {
27849 if (pContext->sndio.sndioSO == NULL) {
27850 return MA_NO_BACKEND;
27853 pContext->sndio.sio_open = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_open");
27854 pContext->sndio.sio_close = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_close");
27855 pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_setpar");
27856 pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getpar");
27857 pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getcap");
27858 pContext->sndio.sio_write = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_write");
27859 pContext->sndio.sio_read = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_read");
27860 pContext->sndio.sio_start = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_start");
27861 pContext->sndio.sio_stop = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_stop");
27862 pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_initpar");
27864 pContext->sndio.sio_open = sio_open;
27865 pContext->sndio.sio_close = sio_close;
27866 pContext->sndio.sio_setpar = sio_setpar;
27867 pContext->sndio.sio_getpar = sio_getpar;
27868 pContext->sndio.sio_getcap = sio_getcap;
27869 pContext->sndio.sio_write = sio_write;
27870 pContext->sndio.sio_read = sio_read;
27871 pContext->sndio.sio_start = sio_start;
27872 pContext->sndio.sio_stop = sio_stop;
27873 pContext->sndio.sio_initpar = sio_initpar;
27876 pCallbacks->onContextInit = ma_context_init__sndio;
27877 pCallbacks->onContextUninit = ma_context_uninit__sndio;
27878 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio;
27879 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sndio;
27880 pCallbacks->onDeviceInit = ma_device_init__sndio;
27881 pCallbacks->onDeviceUninit = ma_device_uninit__sndio;
27882 pCallbacks->onDeviceStart = ma_device_start__sndio;
27883 pCallbacks->onDeviceStop = ma_device_stop__sndio;
27884 pCallbacks->onDeviceRead = ma_device_read__sndio;
27885 pCallbacks->onDeviceWrite = ma_device_write__sndio;
27886 pCallbacks->onDeviceDataLoop = NULL;
27895 /******************************************************************************
27899 ******************************************************************************/
27900 #ifdef MA_HAS_AUDIO4
27904 #include <sys/stat.h>
27905 #include <sys/types.h>
27906 #include <sys/ioctl.h>
27907 #include <sys/audioio.h>
27909 #if defined(__OpenBSD__)
27910 #include <sys/param.h>
27911 #if defined(OpenBSD) && OpenBSD >= 201709
27912 #define MA_AUDIO4_USE_NEW_API
27916 static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex)
27920 MA_ASSERT(id != NULL);
27921 MA_ASSERT(idSize > 0);
27922 MA_ASSERT(deviceIndex >= 0);
27924 baseLen = strlen(base);
27925 MA_ASSERT(idSize > baseLen);
27927 ma_strcpy_s(id, idSize, base);
27928 ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10);
27931 static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut)
27935 const char* deviceIndexStr;
27937 MA_ASSERT(id != NULL);
27938 MA_ASSERT(base != NULL);
27939 MA_ASSERT(pIndexOut != NULL);
27941 idLen = strlen(id);
27942 baseLen = strlen(base);
27943 if (idLen <= baseLen) {
27944 return MA_ERROR; /* Doesn't look like the id starts with the base. */
27947 if (strncmp(id, base, baseLen) != 0) {
27948 return MA_ERROR; /* ID does not begin with base. */
27951 deviceIndexStr = id + baseLen;
27952 if (deviceIndexStr[0] == '\0') {
27953 return MA_ERROR; /* No index specified in the ID. */
27957 *pIndexOut = atoi(deviceIndexStr);
27964 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
27965 static ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision)
27967 if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) {
27968 return ma_format_u8;
27970 if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) {
27971 if (precision == 16) {
27972 return ma_format_s16;
27973 } else if (precision == 24) {
27974 return ma_format_s24;
27975 } else if (precision == 32) {
27976 return ma_format_s32;
27978 } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) {
27979 if (precision == 16) {
27980 return ma_format_s16;
27981 } else if (precision == 24) {
27982 return ma_format_s24;
27983 } else if (precision == 32) {
27984 return ma_format_s32;
27989 return ma_format_unknown; /* Encoding not supported. */
27992 static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision)
27994 MA_ASSERT(pEncoding != NULL);
27995 MA_ASSERT(pPrecision != NULL);
28001 *pEncoding = AUDIO_ENCODING_ULINEAR;
28005 case ma_format_s24:
28007 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
28011 case ma_format_s32:
28013 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
28017 case ma_format_s16:
28018 case ma_format_f32:
28019 case ma_format_unknown:
28022 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
28028 static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo)
28030 return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision);
28033 static ma_format ma_best_format_from_fd__audio4(int fd, ma_format preferredFormat)
28035 audio_encoding_t encoding;
28039 /* First check to see if the preferred format is supported. */
28040 if (preferredFormat != ma_format_unknown) {
28043 MA_ZERO_OBJECT(&encoding);
28044 encoding.index = counter;
28045 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
28049 if (preferredFormat == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) {
28050 return preferredFormat; /* Found the preferred format. */
28053 /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */
28058 /* Getting here means our preferred format is not supported, so fall back to our standard priorities. */
28059 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
28060 ma_format format = g_maFormatPriorities[iFormat];
28064 MA_ZERO_OBJECT(&encoding);
28065 encoding.index = counter;
28066 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
28070 if (format == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) {
28071 return format; /* Found a workable format. */
28074 /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */
28079 /* Getting here means not appropriate format was found. */
28080 return ma_format_unknown;
28083 static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par)
28085 if (par->bits == 8 && par->bps == 1 && par->sig == 0) {
28086 return ma_format_u8;
28088 if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) {
28089 return ma_format_s16;
28091 if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) {
28092 return ma_format_s24;
28094 if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) {
28095 return ma_format_f32;
28098 /* Format not supported. */
28099 return ma_format_unknown;
28103 static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pDeviceInfo)
28105 audio_device_t fdDevice;
28107 MA_ASSERT(pContext != NULL);
28108 MA_ASSERT(fd >= 0);
28109 MA_ASSERT(pDeviceInfo != NULL);
28114 if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) {
28115 return MA_ERROR; /* Failed to retrieve device info. */
28119 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), fdDevice.name);
28121 #if !defined(MA_AUDIO4_USE_NEW_API)
28123 audio_info_t fdInfo;
28125 ma_uint32 channels;
28126 ma_uint32 sampleRate;
28128 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
28132 if (deviceType == ma_device_type_playback) {
28133 channels = fdInfo.play.channels;
28134 sampleRate = fdInfo.play.sample_rate;
28136 channels = fdInfo.record.channels;
28137 sampleRate = fdInfo.record.sample_rate;
28140 /* Supported formats. We get this by looking at the encodings. */
28141 pDeviceInfo->nativeDataFormatCount = 0;
28143 audio_encoding_t encoding;
28146 MA_ZERO_OBJECT(&encoding);
28147 encoding.index = counter;
28148 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
28152 format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision);
28153 if (format != ma_format_unknown) {
28154 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0);
28162 struct audio_swpar fdPar;
28164 ma_uint32 channels;
28165 ma_uint32 sampleRate;
28167 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
28171 format = ma_format_from_swpar__audio4(&fdPar);
28172 if (format == ma_format_unknown) {
28173 return MA_FORMAT_NOT_SUPPORTED;
28176 if (deviceType == ma_device_type_playback) {
28177 channels = fdPar.pchan;
28179 channels = fdPar.rchan;
28182 sampleRate = fdPar.rate;
28184 pDeviceInfo->nativeDataFormatCount = 0;
28185 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0);
28192 static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
28194 const int maxDevices = 64;
28198 MA_ASSERT(pContext != NULL);
28199 MA_ASSERT(callback != NULL);
28202 Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN"
28203 version here since we can open it even when another process has control of the "/dev/audioN" device.
28205 for (iDevice = 0; iDevice < maxDevices; ++iDevice) {
28208 ma_bool32 isTerminating = MA_FALSE;
28210 ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl");
28211 ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10);
28213 if (stat(devpath, &st) < 0) {
28217 /* The device exists, but we need to check if it's usable as playback and/or capture. */
28220 if (!isTerminating) {
28221 fd = open(devpath, O_RDONLY, 0);
28223 /* Supports playback. */
28224 ma_device_info deviceInfo;
28225 MA_ZERO_OBJECT(&deviceInfo);
28226 ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
28227 if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) {
28228 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
28236 if (!isTerminating) {
28237 fd = open(devpath, O_WRONLY, 0);
28239 /* Supports capture. */
28240 ma_device_info deviceInfo;
28241 MA_ZERO_OBJECT(&deviceInfo);
28242 ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
28243 if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) {
28244 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
28251 if (isTerminating) {
28259 static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
28262 int deviceIndex = -1;
28266 MA_ASSERT(pContext != NULL);
28269 We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number
28270 from the device ID which will be in "/dev/audioN" format.
28272 if (pDeviceID == NULL) {
28273 /* Default device. */
28274 ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl");
28276 /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */
28277 result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex);
28278 if (result != MA_SUCCESS) {
28282 ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex);
28285 fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0);
28287 return MA_NO_DEVICE;
28290 if (deviceIndex == -1) {
28291 ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio");
28293 ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex);
28296 result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo);
28302 static ma_result ma_device_uninit__audio4(ma_device* pDevice)
28304 MA_ASSERT(pDevice != NULL);
28306 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28307 close(pDevice->audio4.fdCapture);
28310 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
28311 close(pDevice->audio4.fdPlayback);
28317 static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
28319 const char* pDefaultDeviceNames[] = {
28325 ma_format internalFormat;
28326 ma_uint32 internalChannels;
28327 ma_uint32 internalSampleRate;
28328 ma_uint32 internalPeriodSizeInFrames;
28329 ma_uint32 internalPeriods;
28331 MA_ASSERT(pConfig != NULL);
28332 MA_ASSERT(deviceType != ma_device_type_duplex);
28333 MA_ASSERT(pDevice != NULL);
28335 /* The first thing to do is open the file. */
28336 if (deviceType == ma_device_type_capture) {
28337 fdFlags = O_RDONLY;
28339 fdFlags = O_WRONLY;
28341 /*fdFlags |= O_NONBLOCK;*/
28343 if (pDescriptor->pDeviceID == NULL) {
28344 /* Default device. */
28346 for (iDevice = 0; iDevice < ma_countof(pDefaultDeviceNames); ++iDevice) {
28347 fd = open(pDefaultDeviceNames[iDevice], fdFlags, 0);
28353 /* Specific device. */
28354 fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0);
28358 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device.", ma_result_from_errno(errno));
28361 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
28363 audio_info_t fdInfo;
28366 The documentation is a little bit unclear to me as to how it handles formats. It says the
28369 Regardless of formats supported by underlying driver, the audio driver accepts the
28372 By then the next sentence says this:
28374 `encoding` and `precision` are one of the values obtained by AUDIO_GETENC.
28376 It sounds like a direct contradiction to me. I'm going to play this safe any only use the
28377 best sample format returned by AUDIO_GETENC. If the requested format is supported we'll
28378 use that, but otherwise we'll just use our standard format priorities to pick an
28381 AUDIO_INITINFO(&fdInfo);
28383 /* We get the driver to do as much of the data conversion as possible. */
28384 if (deviceType == ma_device_type_capture) {
28385 fdInfo.mode = AUMODE_RECORD;
28386 ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.record.encoding, &fdInfo.record.precision);
28388 if (pDescriptor->channels != 0) {
28389 fdInfo.record.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */
28392 if (pDescriptor->sampleRate != 0) {
28393 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`.) */
28396 fdInfo.mode = AUMODE_PLAY;
28397 ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.play.encoding, &fdInfo.play.precision);
28399 if (pDescriptor->channels != 0) {
28400 fdInfo.play.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */
28403 if (pDescriptor->sampleRate != 0) {
28404 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`.) */
28408 if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
28410 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
28413 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
28415 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
28418 if (deviceType == ma_device_type_capture) {
28419 internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record);
28420 internalChannels = fdInfo.record.channels;
28421 internalSampleRate = fdInfo.record.sample_rate;
28423 internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play);
28424 internalChannels = fdInfo.play.channels;
28425 internalSampleRate = fdInfo.play.sample_rate;
28428 if (internalFormat == ma_format_unknown) {
28430 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);
28435 ma_uint32 internalPeriodSizeInBytes;
28437 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile);
28439 internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
28440 if (internalPeriodSizeInBytes < 16) {
28441 internalPeriodSizeInBytes = 16;
28444 internalPeriods = pDescriptor->periodCount;
28445 if (internalPeriods < 2) {
28446 internalPeriods = 2;
28449 /* What miniaudio calls a period, audio4 calls a block. */
28450 AUDIO_INITINFO(&fdInfo);
28451 fdInfo.hiwat = internalPeriods;
28452 fdInfo.lowat = internalPeriods-1;
28453 fdInfo.blocksize = internalPeriodSizeInBytes;
28454 if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
28456 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
28459 internalPeriods = fdInfo.hiwat;
28460 internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels);
28465 struct audio_swpar fdPar;
28467 /* 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. */
28468 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
28470 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters.", MA_FORMAT_NOT_SUPPORTED);
28473 internalFormat = ma_format_from_swpar__audio4(&fdPar);
28474 internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
28475 internalSampleRate = fdPar.rate;
28477 if (internalFormat == ma_format_unknown) {
28479 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);
28484 ma_uint32 internalPeriodSizeInBytes;
28486 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile);
28488 /* What miniaudio calls a period, audio4 calls a block. */
28489 internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
28490 if (internalPeriodSizeInBytes < 16) {
28491 internalPeriodSizeInBytes = 16;
28494 fdPar.nblks = pDescriptor->periodCount;
28495 fdPar.round = internalPeriodSizeInBytes;
28497 if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) {
28499 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters.", MA_FORMAT_NOT_SUPPORTED);
28502 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
28504 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters.", MA_FORMAT_NOT_SUPPORTED);
28508 internalFormat = ma_format_from_swpar__audio4(&fdPar);
28509 internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
28510 internalSampleRate = fdPar.rate;
28511 internalPeriods = fdPar.nblks;
28512 internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels);
28516 if (internalFormat == ma_format_unknown) {
28518 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);
28521 if (deviceType == ma_device_type_capture) {
28522 pDevice->audio4.fdCapture = fd;
28524 pDevice->audio4.fdPlayback = fd;
28527 pDescriptor->format = internalFormat;
28528 pDescriptor->channels = internalChannels;
28529 pDescriptor->sampleRate = internalSampleRate;
28530 ma_get_standard_channel_map(ma_standard_channel_map_sound4, internalChannels, pDescriptor->channelMap);
28531 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
28532 pDescriptor->periodCount = internalPeriods;
28537 static ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
28539 MA_ASSERT(pDevice != NULL);
28541 MA_ZERO_OBJECT(&pDevice->audio4);
28543 if (pConfig->deviceType == ma_device_type_loopback) {
28544 return MA_DEVICE_TYPE_NOT_SUPPORTED;
28547 pDevice->audio4.fdCapture = -1;
28548 pDevice->audio4.fdPlayback = -1;
28551 The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD
28552 introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as
28555 #if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000
28557 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
28558 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
28559 return MA_SHARE_MODE_NOT_SUPPORTED;
28562 /* All other flavors. */
28565 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
28566 ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
28567 if (result != MA_SUCCESS) {
28572 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
28573 ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
28574 if (result != MA_SUCCESS) {
28575 if (pConfig->deviceType == ma_device_type_duplex) {
28576 close(pDevice->audio4.fdCapture);
28585 static ma_result ma_device_start__audio4(ma_device* pDevice)
28587 MA_ASSERT(pDevice != NULL);
28589 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28590 if (pDevice->audio4.fdCapture == -1) {
28591 return MA_INVALID_ARGS;
28595 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
28596 if (pDevice->audio4.fdPlayback == -1) {
28597 return MA_INVALID_ARGS;
28604 static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd)
28607 return MA_INVALID_ARGS;
28610 #if !defined(MA_AUDIO4_USE_NEW_API)
28611 if (ioctl(fd, AUDIO_FLUSH, 0) < 0) {
28612 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed.", ma_result_from_errno(errno));
28615 if (ioctl(fd, AUDIO_STOP, 0) < 0) {
28616 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed.", ma_result_from_errno(errno));
28623 static ma_result ma_device_stop__audio4(ma_device* pDevice)
28625 MA_ASSERT(pDevice != NULL);
28627 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28630 result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture);
28631 if (result != MA_SUCCESS) {
28636 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
28639 /* 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. */
28640 #if !defined(MA_AUDIO4_USE_NEW_API)
28641 ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0);
28644 /* Here is where the device is stopped immediately. */
28645 result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback);
28646 if (result != MA_SUCCESS) {
28654 static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
28658 if (pFramesWritten != NULL) {
28659 *pFramesWritten = 0;
28662 result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
28664 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device.", ma_result_from_errno(errno));
28667 if (pFramesWritten != NULL) {
28668 *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
28674 static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
28678 if (pFramesRead != NULL) {
28682 result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
28684 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device.", ma_result_from_errno(errno));
28687 if (pFramesRead != NULL) {
28688 *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
28694 static ma_result ma_context_uninit__audio4(ma_context* pContext)
28696 MA_ASSERT(pContext != NULL);
28697 MA_ASSERT(pContext->backend == ma_backend_audio4);
28703 static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
28705 MA_ASSERT(pContext != NULL);
28709 pCallbacks->onContextInit = ma_context_init__audio4;
28710 pCallbacks->onContextUninit = ma_context_uninit__audio4;
28711 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4;
28712 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audio4;
28713 pCallbacks->onDeviceInit = ma_device_init__audio4;
28714 pCallbacks->onDeviceUninit = ma_device_uninit__audio4;
28715 pCallbacks->onDeviceStart = ma_device_start__audio4;
28716 pCallbacks->onDeviceStop = ma_device_stop__audio4;
28717 pCallbacks->onDeviceRead = ma_device_read__audio4;
28718 pCallbacks->onDeviceWrite = ma_device_write__audio4;
28719 pCallbacks->onDeviceDataLoop = NULL;
28723 #endif /* audio4 */
28726 /******************************************************************************
28730 ******************************************************************************/
28732 #include <sys/ioctl.h>
28733 #include <unistd.h>
28735 #include <sys/soundcard.h>
28737 #ifndef SNDCTL_DSP_HALT
28738 #define SNDCTL_DSP_HALT SNDCTL_DSP_RESET
28741 #define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp"
28743 static int ma_open_temp_device__oss()
28745 /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */
28746 int fd = open("/dev/mixer", O_RDONLY, 0);
28754 static ma_result ma_context_open_device__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, int* pfd)
28756 const char* deviceName;
28759 MA_ASSERT(pContext != NULL);
28760 MA_ASSERT(pfd != NULL);
28765 /* This function should only be called for playback or capture, not duplex. */
28766 if (deviceType == ma_device_type_duplex) {
28767 return MA_INVALID_ARGS;
28770 deviceName = MA_OSS_DEFAULT_DEVICE_NAME;
28771 if (pDeviceID != NULL) {
28772 deviceName = pDeviceID->oss;
28775 flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY;
28776 if (shareMode == ma_share_mode_exclusive) {
28780 *pfd = open(deviceName, flags, 0);
28782 return ma_result_from_errno(errno);
28788 static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
28794 MA_ASSERT(pContext != NULL);
28795 MA_ASSERT(callback != NULL);
28797 fd = ma_open_temp_device__oss();
28799 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);
28802 result = ioctl(fd, SNDCTL_SYSINFO, &si);
28803 if (result != -1) {
28805 for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
28807 ai.dev = iAudioDevice;
28808 result = ioctl(fd, SNDCTL_AUDIOINFO, &ai);
28809 if (result != -1) {
28810 if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */
28811 ma_device_info deviceInfo;
28812 ma_bool32 isTerminating = MA_FALSE;
28814 MA_ZERO_OBJECT(&deviceInfo);
28817 ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1);
28820 The human readable device name should be in the "ai.handle" variable, but it can
28821 sometimes be empty in which case we just fall back to "ai.name" which is less user
28822 friendly, but usually has a value.
28824 if (ai.handle[0] != '\0') {
28825 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1);
28827 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1);
28830 /* The device can be both playback and capture. */
28831 if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) {
28832 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
28834 if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) {
28835 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
28838 if (isTerminating) {
28846 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MA_NO_BACKEND);
28853 static void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo)
28855 unsigned int minChannels;
28856 unsigned int maxChannels;
28857 unsigned int iRate;
28859 MA_ASSERT(pContext != NULL);
28860 MA_ASSERT(pAudioInfo != NULL);
28861 MA_ASSERT(pDeviceInfo != NULL);
28863 /* If we support all channels we just report 0. */
28864 minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
28865 maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
28868 OSS has this annoying thing where sample rates can be reported in two ways. We prefer explicitness,
28869 which OSS has in the form of nrates/rates, however there are times where nrates can be 0, in which
28870 case we'll need to use min_rate and max_rate and report only standard rates.
28872 if (pAudioInfo->nrates > 0) {
28873 for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) {
28874 unsigned int rate = pAudioInfo->rates[iRate];
28876 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
28877 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. */
28879 unsigned int iChannel;
28880 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
28881 ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0);
28886 for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) {
28887 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate];
28889 if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) {
28890 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
28891 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. */
28893 unsigned int iChannel;
28894 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
28895 ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0);
28903 static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
28905 ma_bool32 foundDevice;
28910 MA_ASSERT(pContext != NULL);
28912 /* Handle the default device a little differently. */
28913 if (pDeviceID == NULL) {
28914 if (deviceType == ma_device_type_playback) {
28915 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
28917 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
28924 /* If we get here it means we are _not_ using the default device. */
28925 foundDevice = MA_FALSE;
28927 fdTemp = ma_open_temp_device__oss();
28928 if (fdTemp == -1) {
28929 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);
28932 result = ioctl(fdTemp, SNDCTL_SYSINFO, &si);
28933 if (result != -1) {
28935 for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
28937 ai.dev = iAudioDevice;
28938 result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai);
28939 if (result != -1) {
28940 if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) {
28941 /* It has the same name, so now just confirm the type. */
28942 if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) ||
28943 (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) {
28944 unsigned int formatMask;
28947 ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1);
28950 The human readable device name should be in the "ai.handle" variable, but it can
28951 sometimes be empty in which case we just fall back to "ai.name" which is less user
28952 friendly, but usually has a value.
28954 if (ai.handle[0] != '\0') {
28955 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1);
28957 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1);
28961 pDeviceInfo->nativeDataFormatCount = 0;
28963 if (deviceType == ma_device_type_playback) {
28964 formatMask = ai.oformats;
28966 formatMask = ai.iformats;
28969 if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) {
28970 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo);
28972 if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) {
28973 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo);
28975 if ((formatMask & AFMT_U8) != 0) {
28976 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo);
28979 foundDevice = MA_TRUE;
28987 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MA_NO_BACKEND);
28993 if (!foundDevice) {
28994 return MA_NO_DEVICE;
29000 static ma_result ma_device_uninit__oss(ma_device* pDevice)
29002 MA_ASSERT(pDevice != NULL);
29004 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
29005 close(pDevice->oss.fdCapture);
29008 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
29009 close(pDevice->oss.fdPlayback);
29015 static int ma_format_to_oss(ma_format format)
29017 int ossFormat = AFMT_U8;
29019 case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
29020 case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
29021 case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
29022 case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
29024 default: ossFormat = AFMT_U8; break;
29030 static ma_format ma_format_from_oss(int ossFormat)
29032 if (ossFormat == AFMT_U8) {
29033 return ma_format_u8;
29035 if (ma_is_little_endian()) {
29036 switch (ossFormat) {
29037 case AFMT_S16_LE: return ma_format_s16;
29038 case AFMT_S32_LE: return ma_format_s32;
29039 default: return ma_format_unknown;
29042 switch (ossFormat) {
29043 case AFMT_S16_BE: return ma_format_s16;
29044 case AFMT_S32_BE: return ma_format_s32;
29045 default: return ma_format_unknown;
29050 return ma_format_unknown;
29053 static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
29058 const ma_device_id* pDeviceID = NULL;
29059 ma_share_mode shareMode;
29065 MA_ASSERT(pDevice != NULL);
29066 MA_ASSERT(pConfig != NULL);
29067 MA_ASSERT(deviceType != ma_device_type_duplex);
29069 pDeviceID = pDescriptor->pDeviceID;
29070 shareMode = pDescriptor->shareMode;
29071 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. */
29072 ossChannels = (int)(pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS;
29073 ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE;
29075 result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd);
29076 if (result != MA_SUCCESS) {
29077 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", result);
29081 The OSS documantation is very clear about the order we should be initializing the device's properties:
29088 ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat);
29089 if (ossResult == -1) {
29091 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format.", MA_FORMAT_NOT_SUPPORTED);
29095 ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels);
29096 if (ossResult == -1) {
29098 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count.", MA_FORMAT_NOT_SUPPORTED);
29102 ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate);
29103 if (ossResult == -1) {
29105 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate.", MA_FORMAT_NOT_SUPPORTED);
29111 The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if
29112 it should be done before or after format/channels/rate.
29114 OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual
29118 ma_uint32 periodSizeInFrames;
29119 ma_uint32 periodSizeInBytes;
29120 ma_uint32 ossFragmentSizePower;
29122 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile);
29124 periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels));
29125 if (periodSizeInBytes < 16) {
29126 periodSizeInBytes = 16;
29129 ossFragmentSizePower = 4;
29130 periodSizeInBytes >>= 4;
29131 while (periodSizeInBytes >>= 1) {
29132 ossFragmentSizePower += 1;
29135 ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower);
29136 ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment);
29137 if (ossResult == -1) {
29139 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count.", MA_FORMAT_NOT_SUPPORTED);
29143 /* Internal settings. */
29144 if (deviceType == ma_device_type_capture) {
29145 pDevice->oss.fdCapture = fd;
29147 pDevice->oss.fdPlayback = fd;
29150 pDescriptor->format = ma_format_from_oss(ossFormat);
29151 pDescriptor->channels = ossChannels;
29152 pDescriptor->sampleRate = ossSampleRate;
29153 ma_get_standard_channel_map(ma_standard_channel_map_sound4, pDescriptor->channels, pDescriptor->channelMap);
29154 pDescriptor->periodCount = (ma_uint32)(ossFragment >> 16);
29155 pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels);
29157 if (pDescriptor->format == ma_format_unknown) {
29158 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
29164 static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
29166 MA_ASSERT(pDevice != NULL);
29167 MA_ASSERT(pConfig != NULL);
29169 MA_ZERO_OBJECT(&pDevice->oss);
29171 if (pConfig->deviceType == ma_device_type_loopback) {
29172 return MA_DEVICE_TYPE_NOT_SUPPORTED;
29175 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
29176 ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
29177 if (result != MA_SUCCESS) {
29178 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", result);
29182 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
29183 ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
29184 if (result != MA_SUCCESS) {
29185 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", result);
29193 Note on Starting and Stopping
29194 =============================
29195 In the past I was using SNDCTL_DSP_HALT to stop the device, however this results in issues when
29196 trying to resume the device again. If we use SNDCTL_DSP_HALT, the next write() or read() will
29197 fail. Instead what we need to do is just not write or read to and from the device when the
29198 device is not running.
29200 As a result, both the start and stop functions for OSS are just empty stubs. The starting and
29201 stopping logic is handled by ma_device_write__oss() and ma_device_read__oss(). These will check
29202 the device state, and if the device is stopped they will simply not do any kind of processing.
29204 The downside to this technique is that I've noticed a fairly lengthy delay in stopping the
29205 device, up to a second. This is on a virtual machine, and as such might just be due to the
29206 virtual drivers, but I'm not fully sure. I am not sure how to work around this problem so for
29207 the moment that's just how it's going to have to be.
29209 When starting the device, OSS will automatically start it when write() or read() is called.
29211 static ma_result ma_device_start__oss(ma_device* pDevice)
29213 MA_ASSERT(pDevice != NULL);
29215 /* The device is automatically started with reading and writing. */
29221 static ma_result ma_device_stop__oss(ma_device* pDevice)
29223 MA_ASSERT(pDevice != NULL);
29225 /* See note above on why this is empty. */
29231 static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
29234 ma_uint32 deviceState;
29236 if (pFramesWritten != NULL) {
29237 *pFramesWritten = 0;
29240 /* Don't do any processing if the device is stopped. */
29241 deviceState = ma_device_get_state(pDevice);
29242 if (deviceState != MA_STATE_STARTED && deviceState != MA_STATE_STARTING) {
29246 resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
29247 if (resultOSS < 0) {
29248 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));
29251 if (pFramesWritten != NULL) {
29252 *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
29258 static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
29261 ma_uint32 deviceState;
29263 if (pFramesRead != NULL) {
29267 /* Don't do any processing if the device is stopped. */
29268 deviceState = ma_device_get_state(pDevice);
29269 if (deviceState != MA_STATE_STARTED && deviceState != MA_STATE_STARTING) {
29273 resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
29274 if (resultOSS < 0) {
29275 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));
29278 if (pFramesRead != NULL) {
29279 *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
29285 static ma_result ma_context_uninit__oss(ma_context* pContext)
29287 MA_ASSERT(pContext != NULL);
29288 MA_ASSERT(pContext->backend == ma_backend_oss);
29294 static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
29300 MA_ASSERT(pContext != NULL);
29304 /* Try opening a temporary device first so we can get version information. This is closed at the end. */
29305 fd = ma_open_temp_device__oss();
29307 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. */
29310 /* Grab the OSS version. */
29312 result = ioctl(fd, OSS_GETVERSION, &ossVersion);
29313 if (result == -1) {
29315 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version.", MA_NO_BACKEND);
29318 /* The file handle to temp device is no longer needed. Close ASAP. */
29321 pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16);
29322 pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8);
29324 pCallbacks->onContextInit = ma_context_init__oss;
29325 pCallbacks->onContextUninit = ma_context_uninit__oss;
29326 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss;
29327 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss;
29328 pCallbacks->onDeviceInit = ma_device_init__oss;
29329 pCallbacks->onDeviceUninit = ma_device_uninit__oss;
29330 pCallbacks->onDeviceStart = ma_device_start__oss;
29331 pCallbacks->onDeviceStop = ma_device_stop__oss;
29332 pCallbacks->onDeviceRead = ma_device_read__oss;
29333 pCallbacks->onDeviceWrite = ma_device_write__oss;
29334 pCallbacks->onDeviceDataLoop = NULL;
29341 /******************************************************************************
29345 ******************************************************************************/
29346 #ifdef MA_HAS_AAUDIO
29348 /*#include <AAudio/AAudio.h>*/
29350 typedef int32_t ma_aaudio_result_t;
29351 typedef int32_t ma_aaudio_direction_t;
29352 typedef int32_t ma_aaudio_sharing_mode_t;
29353 typedef int32_t ma_aaudio_format_t;
29354 typedef int32_t ma_aaudio_stream_state_t;
29355 typedef int32_t ma_aaudio_performance_mode_t;
29356 typedef int32_t ma_aaudio_usage_t;
29357 typedef int32_t ma_aaudio_content_type_t;
29358 typedef int32_t ma_aaudio_input_preset_t;
29359 typedef int32_t ma_aaudio_data_callback_result_t;
29360 typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder;
29361 typedef struct ma_AAudioStream_t* ma_AAudioStream;
29363 #define MA_AAUDIO_UNSPECIFIED 0
29365 /* Result codes. miniaudio only cares about the success code. */
29366 #define MA_AAUDIO_OK 0
29369 #define MA_AAUDIO_DIRECTION_OUTPUT 0
29370 #define MA_AAUDIO_DIRECTION_INPUT 1
29372 /* Sharing modes. */
29373 #define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0
29374 #define MA_AAUDIO_SHARING_MODE_SHARED 1
29377 #define MA_AAUDIO_FORMAT_PCM_I16 1
29378 #define MA_AAUDIO_FORMAT_PCM_FLOAT 2
29380 /* Stream states. */
29381 #define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0
29382 #define MA_AAUDIO_STREAM_STATE_UNKNOWN 1
29383 #define MA_AAUDIO_STREAM_STATE_OPEN 2
29384 #define MA_AAUDIO_STREAM_STATE_STARTING 3
29385 #define MA_AAUDIO_STREAM_STATE_STARTED 4
29386 #define MA_AAUDIO_STREAM_STATE_PAUSING 5
29387 #define MA_AAUDIO_STREAM_STATE_PAUSED 6
29388 #define MA_AAUDIO_STREAM_STATE_FLUSHING 7
29389 #define MA_AAUDIO_STREAM_STATE_FLUSHED 8
29390 #define MA_AAUDIO_STREAM_STATE_STOPPING 9
29391 #define MA_AAUDIO_STREAM_STATE_STOPPED 10
29392 #define MA_AAUDIO_STREAM_STATE_CLOSING 11
29393 #define MA_AAUDIO_STREAM_STATE_CLOSED 12
29394 #define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13
29396 /* Performance modes. */
29397 #define MA_AAUDIO_PERFORMANCE_MODE_NONE 10
29398 #define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11
29399 #define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12
29402 #define MA_AAUDIO_USAGE_MEDIA 1
29403 #define MA_AAUDIO_USAGE_VOICE_COMMUNICATION 2
29404 #define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING 3
29405 #define MA_AAUDIO_USAGE_ALARM 4
29406 #define MA_AAUDIO_USAGE_NOTIFICATION 5
29407 #define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE 6
29408 #define MA_AAUDIO_USAGE_NOTIFICATION_EVENT 10
29409 #define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY 11
29410 #define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 12
29411 #define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION 13
29412 #define MA_AAUDIO_USAGE_GAME 14
29413 #define MA_AAUDIO_USAGE_ASSISTANT 16
29414 #define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY 1000
29415 #define MA_AAUDIO_SYSTEM_USAGE_SAFETY 1001
29416 #define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS 1002
29417 #define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT 1003
29419 /* Content types. */
29420 #define MA_AAUDIO_CONTENT_TYPE_SPEECH 1
29421 #define MA_AAUDIO_CONTENT_TYPE_MUSIC 2
29422 #define MA_AAUDIO_CONTENT_TYPE_MOVIE 3
29423 #define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4
29425 /* Input presets. */
29426 #define MA_AAUDIO_INPUT_PRESET_GENERIC 1
29427 #define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5
29428 #define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6
29429 #define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7
29430 #define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9
29431 #define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10
29433 /* Callback results. */
29434 #define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0
29435 #define MA_AAUDIO_CALLBACK_RESULT_STOP 1
29438 typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames);
29439 typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error);
29441 typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder);
29442 typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder);
29443 typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId);
29444 typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction);
29445 typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode);
29446 typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format);
29447 typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount);
29448 typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate);
29449 typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
29450 typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
29451 typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData);
29452 typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData);
29453 typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode);
29454 typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType);
29455 typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType);
29456 typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset);
29457 typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream);
29458 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream);
29459 typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream);
29460 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_waitForStateChange) (ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds);
29461 typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream);
29462 typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream);
29463 typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream);
29464 typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream);
29465 typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream);
29466 typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream);
29467 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream);
29468 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream);
29470 static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA)
29474 case MA_AAUDIO_OK: return MA_SUCCESS;
29481 static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage)
29484 case ma_aaudio_usage_announcement: return MA_AAUDIO_USAGE_MEDIA;
29485 case ma_aaudio_usage_emergency: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION;
29486 case ma_aaudio_usage_safety: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
29487 case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_USAGE_ALARM;
29488 case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_NOTIFICATION;
29489 case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE;
29490 case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT;
29491 case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
29492 case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
29493 case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION;
29494 case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_GAME;
29495 case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_ASSISTANT;
29496 case ma_aaudio_usage_notification_event: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY;
29497 case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_SYSTEM_USAGE_SAFETY;
29498 case ma_aaudio_usage_voice_communication: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS;
29499 case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT;
29503 return MA_AAUDIO_USAGE_MEDIA;
29506 static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType)
29508 switch (contentType) {
29509 case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE;
29510 case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC;
29511 case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION;
29512 case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH;
29516 return MA_AAUDIO_CONTENT_TYPE_SPEECH;
29519 static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset)
29521 switch (inputPreset) {
29522 case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC;
29523 case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER;
29524 case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED;
29525 case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION;
29526 case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION;
29527 case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE;
29531 return MA_AAUDIO_INPUT_PRESET_GENERIC;
29534 static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error)
29536 ma_device* pDevice = (ma_device*)pUserData;
29537 MA_ASSERT(pDevice != NULL);
29541 #if defined(MA_DEBUG_OUTPUT)
29542 printf("[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream));
29546 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
29547 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.
29549 if (((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream) == MA_AAUDIO_STREAM_STATE_DISCONNECTED) {
29550 #if defined(MA_DEBUG_OUTPUT)
29551 printf("[AAudio] Device Disconnected.\n");
29556 static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
29558 ma_device* pDevice = (ma_device*)pUserData;
29559 MA_ASSERT(pDevice != NULL);
29561 ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, frameCount);
29564 return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
29567 static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
29569 ma_device* pDevice = (ma_device*)pUserData;
29570 MA_ASSERT(pDevice != NULL);
29572 ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, frameCount);
29575 return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
29578 static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, const ma_device_descriptor* pDescriptor, const ma_device_config* pConfig, ma_device* pDevice, ma_AAudioStreamBuilder** ppBuilder)
29580 ma_AAudioStreamBuilder* pBuilder;
29581 ma_aaudio_result_t resultAA;
29582 ma_uint32 bufferCapacityInFrames;
29587 resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder);
29588 if (resultAA != MA_AAUDIO_OK) {
29589 return ma_result_from_aaudio(resultAA);
29592 if (pDeviceID != NULL) {
29593 ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio);
29596 ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT);
29597 ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE);
29600 /* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */
29601 if (pDescriptor != NULL) {
29602 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. */
29604 if (pDescriptor->sampleRate != 0) {
29605 ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate);
29608 if (deviceType == ma_device_type_capture) {
29609 if (pDescriptor->channels != 0) {
29610 ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);
29612 if (pDescriptor->format != ma_format_unknown) {
29613 ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
29616 if (pDescriptor->channels != 0) {
29617 ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);
29619 if (pDescriptor->format != ma_format_unknown) {
29620 ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
29625 AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you
29626 retrieve the actual sample rate until after you've opened the stream. But you need to configure
29627 the buffer capacity before you open the stream... :/
29629 To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on.
29631 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount;
29633 ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames);
29634 ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount);
29636 if (deviceType == ma_device_type_capture) {
29637 if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) {
29638 ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset));
29641 ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice);
29643 if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) {
29644 ((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage));
29647 if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) {
29648 ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType));
29651 ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice);
29654 /* 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. */
29655 ((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);
29657 /* We need to set an error callback to detect device changes. */
29658 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. */
29659 ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice);
29663 *ppBuilder = pBuilder;
29668 static ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream)
29672 result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream));
29673 ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
29678 static ma_result ma_open_stream_basic__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, ma_AAudioStream** ppStream)
29681 ma_AAudioStreamBuilder* pBuilder;
29685 result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder);
29686 if (result != MA_SUCCESS) {
29690 return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream);
29693 static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, const ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream)
29696 ma_AAudioStreamBuilder* pBuilder;
29698 MA_ASSERT(pConfig != NULL);
29699 MA_ASSERT(pConfig->deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */
29703 result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder);
29704 if (result != MA_SUCCESS) {
29708 return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream);
29711 static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream)
29713 return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream));
29716 static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType)
29718 /* The only way to know this is to try creating a stream. */
29719 ma_AAudioStream* pStream;
29720 ma_result result = ma_open_stream_basic__aaudio(pContext, NULL, deviceType, ma_share_mode_shared, &pStream);
29721 if (result != MA_SUCCESS) {
29725 ma_close_stream__aaudio(pContext, pStream);
29729 static ma_result ma_wait_for_simple_state_transition__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_aaudio_stream_state_t oldState, ma_aaudio_stream_state_t newState)
29731 ma_aaudio_stream_state_t actualNewState;
29732 ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */
29733 if (resultAA != MA_AAUDIO_OK) {
29734 return ma_result_from_aaudio(resultAA);
29737 if (newState != actualNewState) {
29738 return MA_ERROR; /* Failed to transition into the expected state. */
29745 static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
29747 ma_bool32 cbResult = MA_TRUE;
29749 MA_ASSERT(pContext != NULL);
29750 MA_ASSERT(callback != NULL);
29752 /* 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. */
29756 ma_device_info deviceInfo;
29757 MA_ZERO_OBJECT(&deviceInfo);
29758 deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
29759 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
29761 if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) {
29762 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
29768 ma_device_info deviceInfo;
29769 MA_ZERO_OBJECT(&deviceInfo);
29770 deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
29771 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
29773 if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) {
29774 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
29781 static void ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_format format, ma_uint32 flags, ma_device_info* pDeviceInfo)
29783 MA_ASSERT(pContext != NULL);
29784 MA_ASSERT(pStream != NULL);
29785 MA_ASSERT(pDeviceInfo != NULL);
29787 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
29788 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream);
29789 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream);
29790 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
29791 pDeviceInfo->nativeDataFormatCount += 1;
29794 static void ma_context_add_native_data_format_from_AAudioStream__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_uint32 flags, ma_device_info* pDeviceInfo)
29796 /* AAudio supports s16 and f32. */
29797 ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_f32, flags, pDeviceInfo);
29798 ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_s16, flags, pDeviceInfo);
29801 static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
29803 ma_AAudioStream* pStream;
29806 MA_ASSERT(pContext != NULL);
29809 if (pDeviceID != NULL) {
29810 pDeviceInfo->id.aaudio = pDeviceID->aaudio;
29812 pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED;
29816 if (deviceType == ma_device_type_playback) {
29817 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
29819 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
29823 pDeviceInfo->nativeDataFormatCount = 0;
29825 /* We'll need to open the device to get accurate sample rate and channel count information. */
29826 result = ma_open_stream_basic__aaudio(pContext, pDeviceID, deviceType, ma_share_mode_shared, &pStream);
29827 if (result != MA_SUCCESS) {
29831 ma_context_add_native_data_format_from_AAudioStream__aaudio(pContext, pStream, 0, pDeviceInfo);
29833 ma_close_stream__aaudio(pContext, pStream);
29840 static ma_result ma_device_uninit__aaudio(ma_device* pDevice)
29842 MA_ASSERT(pDevice != NULL);
29844 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
29845 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
29846 pDevice->aaudio.pStreamCapture = NULL;
29849 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
29850 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
29851 pDevice->aaudio.pStreamPlayback = NULL;
29857 static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream)
29860 int32_t bufferCapacityInFrames;
29861 int32_t framesPerDataCallback;
29862 ma_AAudioStream* pStream;
29864 MA_ASSERT(pDevice != NULL);
29865 MA_ASSERT(pConfig != NULL);
29866 MA_ASSERT(pDescriptor != NULL);
29868 *ppStream = NULL; /* Safety. */
29870 /* First step is to open the stream. From there we'll be able to extract the internal configuration. */
29871 result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream);
29872 if (result != MA_SUCCESS) {
29873 return result; /* Failed to open the AAudio stream. */
29876 /* Now extract the internal configuration. */
29877 pDescriptor->format = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32;
29878 pDescriptor->channels = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream);
29879 pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream);
29881 /* For the channel map we need to be sure we don't overflow any buffers. */
29882 if (pDescriptor->channels <= MA_MAX_CHANNELS) {
29883 ma_get_standard_channel_map(ma_standard_channel_map_default, pDescriptor->channels, pDescriptor->channelMap); /* <-- Cannot find info on channel order, so assuming a default. */
29885 ma_channel_map_init_blank(MA_MAX_CHANNELS, pDescriptor->channelMap); /* Too many channels. Use a blank channel map. */
29888 bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream);
29889 framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream);
29891 if (framesPerDataCallback > 0) {
29892 pDescriptor->periodSizeInFrames = framesPerDataCallback;
29893 pDescriptor->periodCount = bufferCapacityInFrames / framesPerDataCallback;
29895 pDescriptor->periodSizeInFrames = bufferCapacityInFrames;
29896 pDescriptor->periodCount = 1;
29899 *ppStream = pStream;
29904 static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
29908 MA_ASSERT(pDevice != NULL);
29910 if (pConfig->deviceType == ma_device_type_loopback) {
29911 return MA_DEVICE_TYPE_NOT_SUPPORTED;
29914 /* No exclusive mode with AAudio. */
29915 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
29916 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
29917 return MA_SHARE_MODE_NOT_SUPPORTED;
29920 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
29921 result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture);
29922 if (result != MA_SUCCESS) {
29927 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
29928 result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback);
29929 if (result != MA_SUCCESS) {
29937 static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
29939 ma_aaudio_result_t resultAA;
29940 ma_aaudio_stream_state_t currentState;
29942 MA_ASSERT(pDevice != NULL);
29944 resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream);
29945 if (resultAA != MA_AAUDIO_OK) {
29946 return ma_result_from_aaudio(resultAA);
29949 /* Do we actually need to wait for the device to transition into it's started state? */
29951 /* 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. */
29952 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
29953 if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) {
29956 if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) {
29957 return MA_ERROR; /* Expecting the stream to be a starting or started state. */
29960 result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED);
29961 if (result != MA_SUCCESS) {
29969 static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
29971 ma_aaudio_result_t resultAA;
29972 ma_aaudio_stream_state_t currentState;
29974 MA_ASSERT(pDevice != NULL);
29977 From the AAudio documentation:
29979 The stream will stop after all of the data currently buffered has been played.
29981 This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic.
29984 resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream);
29985 if (resultAA != MA_AAUDIO_OK) {
29986 return ma_result_from_aaudio(resultAA);
29989 /* 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. */
29990 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
29991 if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) {
29994 if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) {
29995 return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */
29998 result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED);
29999 if (result != MA_SUCCESS) {
30007 static ma_result ma_device_start__aaudio(ma_device* pDevice)
30009 MA_ASSERT(pDevice != NULL);
30011 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
30012 ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
30013 if (result != MA_SUCCESS) {
30018 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
30019 ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
30020 if (result != MA_SUCCESS) {
30021 if (pDevice->type == ma_device_type_duplex) {
30022 ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
30031 static ma_result ma_device_stop__aaudio(ma_device* pDevice)
30033 ma_stop_proc onStop;
30035 MA_ASSERT(pDevice != NULL);
30037 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
30038 ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
30039 if (result != MA_SUCCESS) {
30044 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
30045 ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
30046 if (result != MA_SUCCESS) {
30051 onStop = pDevice->onStop;
30060 static ma_result ma_context_uninit__aaudio(ma_context* pContext)
30062 MA_ASSERT(pContext != NULL);
30063 MA_ASSERT(pContext->backend == ma_backend_aaudio);
30065 ma_dlclose(pContext, pContext->aaudio.hAAudio);
30066 pContext->aaudio.hAAudio = NULL;
30071 static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
30074 const char* libNames[] = {
30078 for (i = 0; i < ma_countof(libNames); ++i) {
30079 pContext->aaudio.hAAudio = ma_dlopen(pContext, libNames[i]);
30080 if (pContext->aaudio.hAAudio != NULL) {
30085 if (pContext->aaudio.hAAudio == NULL) {
30086 return MA_FAILED_TO_INIT_BACKEND;
30089 pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudio_createStreamBuilder");
30090 pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete");
30091 pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId");
30092 pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection");
30093 pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode");
30094 pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat");
30095 pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount");
30096 pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate");
30097 pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames");
30098 pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback");
30099 pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback");
30100 pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback");
30101 pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode");
30102 pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage");
30103 pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType");
30104 pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset");
30105 pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream");
30106 pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close");
30107 pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState");
30108 pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange");
30109 pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFormat");
30110 pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getChannelCount");
30111 pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getSampleRate");
30112 pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames");
30113 pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback");
30114 pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst");
30115 pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStart");
30116 pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStop");
30119 pCallbacks->onContextInit = ma_context_init__aaudio;
30120 pCallbacks->onContextUninit = ma_context_uninit__aaudio;
30121 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio;
30122 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio;
30123 pCallbacks->onDeviceInit = ma_device_init__aaudio;
30124 pCallbacks->onDeviceUninit = ma_device_uninit__aaudio;
30125 pCallbacks->onDeviceStart = ma_device_start__aaudio;
30126 pCallbacks->onDeviceStop = ma_device_stop__aaudio;
30127 pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */
30128 pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */
30129 pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */
30134 #endif /* AAudio */
30137 /******************************************************************************
30141 ******************************************************************************/
30142 #ifdef MA_HAS_OPENSL
30143 #include <SLES/OpenSLES.h>
30145 #include <SLES/OpenSLES_Android.h>
30148 typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired);
30150 /* OpenSL|ES has one-per-application objects :( */
30151 static SLObjectItf g_maEngineObjectSL = NULL;
30152 static SLEngineItf g_maEngineSL = NULL;
30153 static ma_uint32 g_maOpenSLInitCounter = 0;
30154 static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */
30156 #define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p)))
30157 #define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p)))
30158 #define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p)))
30159 #define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p)))
30162 #define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p)))
30164 #define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p)))
30167 static ma_result ma_result_from_OpenSL(SLuint32 result)
30171 case SL_RESULT_SUCCESS: return MA_SUCCESS;
30172 case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR;
30173 case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS;
30174 case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY;
30175 case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA;
30176 case SL_RESULT_RESOURCE_LOST: return MA_ERROR;
30177 case SL_RESULT_IO_ERROR: return MA_IO_ERROR;
30178 case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE;
30179 case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA;
30180 case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED;
30181 case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR;
30182 case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED;
30183 case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED;
30184 case SL_RESULT_INTERNAL_ERROR: return MA_ERROR;
30185 case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR;
30186 case SL_RESULT_OPERATION_ABORTED: return MA_ERROR;
30187 case SL_RESULT_CONTROL_LOST: return MA_ERROR;
30188 default: return MA_ERROR;
30192 /* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
30193 static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id)
30197 case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
30198 case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
30199 case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
30200 case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
30201 case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
30202 case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
30203 case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
30204 case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
30205 case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
30206 case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
30207 case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
30208 case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
30209 case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
30210 case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
30211 case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
30212 case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
30213 case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
30214 case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
30219 /* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */
30220 static SLuint32 ma_channel_id_to_opensl(ma_uint8 id)
30224 case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER;
30225 case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT;
30226 case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT;
30227 case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER;
30228 case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY;
30229 case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT;
30230 case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT;
30231 case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER;
30232 case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER;
30233 case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER;
30234 case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT;
30235 case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT;
30236 case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER;
30237 case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT;
30238 case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER;
30239 case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT;
30240 case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT;
30241 case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER;
30242 case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT;
30247 /* Converts a channel mapping to an OpenSL-style channel mask. */
30248 static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels)
30250 SLuint32 channelMask = 0;
30251 ma_uint32 iChannel;
30252 for (iChannel = 0; iChannel < channels; ++iChannel) {
30253 channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]);
30256 return channelMask;
30259 /* Converts an OpenSL-style channel mask to a miniaudio channel map. */
30260 static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap)
30262 if (channels == 1 && channelMask == 0) {
30263 pChannelMap[0] = MA_CHANNEL_MONO;
30264 } else if (channels == 2 && channelMask == 0) {
30265 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
30266 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
30268 if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) {
30269 pChannelMap[0] = MA_CHANNEL_MONO;
30271 /* Just iterate over each bit. */
30272 ma_uint32 iChannel = 0;
30274 for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) {
30275 SLuint32 bitValue = (channelMask & (1UL << iBit));
30276 if (bitValue != 0) {
30277 /* The bit is set. */
30278 pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue);
30286 static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec)
30288 if (samplesPerSec <= SL_SAMPLINGRATE_8) {
30289 return SL_SAMPLINGRATE_8;
30291 if (samplesPerSec <= SL_SAMPLINGRATE_11_025) {
30292 return SL_SAMPLINGRATE_11_025;
30294 if (samplesPerSec <= SL_SAMPLINGRATE_12) {
30295 return SL_SAMPLINGRATE_12;
30297 if (samplesPerSec <= SL_SAMPLINGRATE_16) {
30298 return SL_SAMPLINGRATE_16;
30300 if (samplesPerSec <= SL_SAMPLINGRATE_22_05) {
30301 return SL_SAMPLINGRATE_22_05;
30303 if (samplesPerSec <= SL_SAMPLINGRATE_24) {
30304 return SL_SAMPLINGRATE_24;
30306 if (samplesPerSec <= SL_SAMPLINGRATE_32) {
30307 return SL_SAMPLINGRATE_32;
30309 if (samplesPerSec <= SL_SAMPLINGRATE_44_1) {
30310 return SL_SAMPLINGRATE_44_1;
30312 if (samplesPerSec <= SL_SAMPLINGRATE_48) {
30313 return SL_SAMPLINGRATE_48;
30316 /* Android doesn't support more than 48000. */
30318 if (samplesPerSec <= SL_SAMPLINGRATE_64) {
30319 return SL_SAMPLINGRATE_64;
30321 if (samplesPerSec <= SL_SAMPLINGRATE_88_2) {
30322 return SL_SAMPLINGRATE_88_2;
30324 if (samplesPerSec <= SL_SAMPLINGRATE_96) {
30325 return SL_SAMPLINGRATE_96;
30327 if (samplesPerSec <= SL_SAMPLINGRATE_192) {
30328 return SL_SAMPLINGRATE_192;
30332 return SL_SAMPLINGRATE_16;
30336 static SLint32 ma_to_stream_type__opensl(ma_opensl_stream_type streamType)
30338 switch (streamType) {
30339 case ma_opensl_stream_type_voice: return SL_ANDROID_STREAM_VOICE;
30340 case ma_opensl_stream_type_system: return SL_ANDROID_STREAM_SYSTEM;
30341 case ma_opensl_stream_type_ring: return SL_ANDROID_STREAM_RING;
30342 case ma_opensl_stream_type_media: return SL_ANDROID_STREAM_MEDIA;
30343 case ma_opensl_stream_type_alarm: return SL_ANDROID_STREAM_ALARM;
30344 case ma_opensl_stream_type_notification: return SL_ANDROID_STREAM_NOTIFICATION;
30348 return SL_ANDROID_STREAM_VOICE;
30351 static SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordingPreset)
30353 switch (recordingPreset) {
30354 case ma_opensl_recording_preset_generic: return SL_ANDROID_RECORDING_PRESET_GENERIC;
30355 case ma_opensl_recording_preset_camcorder: return SL_ANDROID_RECORDING_PRESET_CAMCORDER;
30356 case ma_opensl_recording_preset_voice_recognition: return SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
30357 case ma_opensl_recording_preset_voice_communication: return SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION;
30358 case ma_opensl_recording_preset_voice_unprocessed: return SL_ANDROID_RECORDING_PRESET_UNPROCESSED;
30362 return SL_ANDROID_RECORDING_PRESET_NONE;
30366 static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
30368 ma_bool32 cbResult;
30370 MA_ASSERT(pContext != NULL);
30371 MA_ASSERT(callback != NULL);
30373 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. */
30374 if (g_maOpenSLInitCounter == 0) {
30375 return MA_INVALID_OPERATION;
30381 This is currently untested, so for now we are just returning default devices.
30383 #if 0 && !defined(MA_ANDROID)
30384 ma_bool32 isTerminated = MA_FALSE;
30386 SLuint32 pDeviceIDs[128];
30387 SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]);
30389 SLAudioIODeviceCapabilitiesItf deviceCaps;
30390 SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
30391 if (resultSL != SL_RESULT_SUCCESS) {
30392 /* The interface may not be supported so just report a default device. */
30393 goto return_default_device;
30397 if (!isTerminated) {
30398 resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs);
30399 if (resultSL != SL_RESULT_SUCCESS) {
30400 return ma_result_from_OpenSL(resultSL);
30403 for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
30404 ma_device_info deviceInfo;
30405 MA_ZERO_OBJECT(&deviceInfo);
30406 deviceInfo.id.opensl = pDeviceIDs[iDevice];
30408 SLAudioOutputDescriptor desc;
30409 resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
30410 if (resultSL == SL_RESULT_SUCCESS) {
30411 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1);
30413 ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
30414 if (cbResult == MA_FALSE) {
30415 isTerminated = MA_TRUE;
30423 if (!isTerminated) {
30424 resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs);
30425 if (resultSL != SL_RESULT_SUCCESS) {
30426 return ma_result_from_OpenSL(resultSL);
30429 for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
30430 ma_device_info deviceInfo;
30431 MA_ZERO_OBJECT(&deviceInfo);
30432 deviceInfo.id.opensl = pDeviceIDs[iDevice];
30434 SLAudioInputDescriptor desc;
30435 resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
30436 if (resultSL == SL_RESULT_SUCCESS) {
30437 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1);
30439 ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
30440 if (cbResult == MA_FALSE) {
30441 isTerminated = MA_TRUE;
30450 goto return_default_device;
30453 return_default_device:;
30454 cbResult = MA_TRUE;
30458 ma_device_info deviceInfo;
30459 MA_ZERO_OBJECT(&deviceInfo);
30460 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
30461 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
30466 ma_device_info deviceInfo;
30467 MA_ZERO_OBJECT(&deviceInfo);
30468 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
30469 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
30475 static void ma_context_add_data_format_ex__opensl(ma_context* pContext, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_device_info* pDeviceInfo)
30477 MA_ASSERT(pContext != NULL);
30478 MA_ASSERT(pDeviceInfo != NULL);
30480 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
30481 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
30482 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
30483 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
30484 pDeviceInfo->nativeDataFormatCount += 1;
30487 static void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo)
30489 ma_uint32 minChannels = 1;
30490 ma_uint32 maxChannels = 2;
30491 ma_uint32 minSampleRate = (ma_uint32)ma_standard_sample_rate_8000;
30492 ma_uint32 maxSampleRate = (ma_uint32)ma_standard_sample_rate_48000;
30493 ma_uint32 iChannel;
30494 ma_uint32 iSampleRate;
30496 MA_ASSERT(pContext != NULL);
30497 MA_ASSERT(pDeviceInfo != NULL);
30500 Each sample format can support mono and stereo, and we'll support a small subset of standard
30501 rates (up to 48000). A better solution would be to somehow find a native sample rate.
30503 for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) {
30504 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) {
30505 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate];
30506 if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) {
30507 ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo);
30513 static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
30515 MA_ASSERT(pContext != NULL);
30517 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. */
30518 if (g_maOpenSLInitCounter == 0) {
30519 return MA_INVALID_OPERATION;
30525 This is currently untested, so for now we are just returning default devices.
30527 #if 0 && !defined(MA_ANDROID)
30528 SLAudioIODeviceCapabilitiesItf deviceCaps;
30529 SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
30530 if (resultSL != SL_RESULT_SUCCESS) {
30531 /* The interface may not be supported so just report a default device. */
30532 goto return_default_device;
30535 if (deviceType == ma_device_type_playback) {
30536 SLAudioOutputDescriptor desc;
30537 resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
30538 if (resultSL != SL_RESULT_SUCCESS) {
30539 return ma_result_from_OpenSL(resultSL);
30542 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1);
30544 SLAudioInputDescriptor desc;
30545 resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
30546 if (resultSL != SL_RESULT_SUCCESS) {
30547 return ma_result_from_OpenSL(resultSL);
30550 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1);
30553 goto return_detailed_info;
30555 goto return_default_device;
30558 return_default_device:
30559 if (pDeviceID != NULL) {
30560 if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) ||
30561 (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) {
30562 return MA_NO_DEVICE; /* Don't know the device. */
30566 /* Name / Description */
30567 if (deviceType == ma_device_type_playback) {
30568 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
30570 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
30573 pDeviceInfo->isDefault = MA_TRUE;
30575 goto return_detailed_info;
30578 return_detailed_info:
30581 For now we're just outputting a set of values that are supported by the API but not necessarily supported
30582 by the device natively. Later on we should work on this so that it more closely reflects the device's
30583 actual native format.
30585 pDeviceInfo->nativeDataFormatCount = 0;
30586 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
30587 ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo);
30589 ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo);
30590 ma_context_add_data_format__opensl(pContext, ma_format_u8, pDeviceInfo);
30597 /*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/
30598 static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
30600 ma_device* pDevice = (ma_device*)pUserData;
30601 size_t periodSizeInBytes;
30605 MA_ASSERT(pDevice != NULL);
30607 (void)pBufferQueue;
30610 For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like
30611 OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this,
30612 but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :(
30615 /* Don't do anything if the device is not started. */
30616 if (ma_device_get_state(pDevice) != MA_STATE_STARTED) {
30620 /* Don't do anything if the device is being drained. */
30621 if (pDevice->opensl.isDrainingCapture) {
30625 periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
30626 pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes);
30628 ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, pDevice->capture.internalPeriodSizeInFrames);
30630 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes);
30631 if (resultSL != SL_RESULT_SUCCESS) {
30635 pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods;
30638 static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
30640 ma_device* pDevice = (ma_device*)pUserData;
30641 size_t periodSizeInBytes;
30645 MA_ASSERT(pDevice != NULL);
30647 (void)pBufferQueue;
30649 /* Don't do anything if the device is not started. */
30650 if (ma_device_get_state(pDevice) != MA_STATE_STARTED) {
30654 /* Don't do anything if the device is being drained. */
30655 if (pDevice->opensl.isDrainingPlayback) {
30659 periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
30660 pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes);
30662 ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, pDevice->playback.internalPeriodSizeInFrames);
30664 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes);
30665 if (resultSL != SL_RESULT_SUCCESS) {
30669 pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods;
30673 static ma_result ma_device_uninit__opensl(ma_device* pDevice)
30675 MA_ASSERT(pDevice != NULL);
30677 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. */
30678 if (g_maOpenSLInitCounter == 0) {
30679 return MA_INVALID_OPERATION;
30682 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
30683 if (pDevice->opensl.pAudioRecorderObj) {
30684 MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj);
30687 ma__free_from_callbacks(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks);
30690 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
30691 if (pDevice->opensl.pAudioPlayerObj) {
30692 MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj);
30694 if (pDevice->opensl.pOutputMixObj) {
30695 MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj);
30698 ma__free_from_callbacks(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks);
30704 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
30705 typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM;
30707 typedef SLDataFormat_PCM ma_SLDataFormat_PCM;
30710 static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat)
30712 /* We need to convert our format/channels/rate so that they aren't set to default. */
30713 if (format == ma_format_unknown) {
30714 format = MA_DEFAULT_FORMAT;
30716 if (channels == 0) {
30717 channels = MA_DEFAULT_CHANNELS;
30719 if (sampleRate == 0) {
30720 sampleRate = MA_DEFAULT_SAMPLE_RATE;
30723 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
30724 if (format == ma_format_f32) {
30725 pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
30726 pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
30728 pDataFormat->formatType = SL_DATAFORMAT_PCM;
30731 pDataFormat->formatType = SL_DATAFORMAT_PCM;
30734 pDataFormat->numChannels = channels;
30735 ((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 */
30736 pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format)*8;
30737 pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels);
30738 pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
30741 Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html
30742 - Only mono and stereo is supported.
30743 - Only u8 and s16 formats are supported.
30744 - Maximum sample rate of 48000.
30747 if (pDataFormat->numChannels > 2) {
30748 pDataFormat->numChannels = 2;
30750 #if __ANDROID_API__ >= 21
30751 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
30752 /* It's floating point. */
30753 MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
30754 if (pDataFormat->bitsPerSample > 32) {
30755 pDataFormat->bitsPerSample = 32;
30758 if (pDataFormat->bitsPerSample > 16) {
30759 pDataFormat->bitsPerSample = 16;
30763 if (pDataFormat->bitsPerSample > 16) {
30764 pDataFormat->bitsPerSample = 16;
30767 if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) {
30768 ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48;
30772 pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */
30777 static ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
30779 ma_bool32 isFloatingPoint = MA_FALSE;
30780 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
30781 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
30782 MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
30783 isFloatingPoint = MA_TRUE;
30786 if (isFloatingPoint) {
30787 if (pDataFormat->bitsPerSample == 32) {
30788 *pFormat = ma_format_f32;
30791 if (pDataFormat->bitsPerSample == 8) {
30792 *pFormat = ma_format_u8;
30793 } else if (pDataFormat->bitsPerSample == 16) {
30794 *pFormat = ma_format_s16;
30795 } else if (pDataFormat->bitsPerSample == 24) {
30796 *pFormat = ma_format_s24;
30797 } else if (pDataFormat->bitsPerSample == 32) {
30798 *pFormat = ma_format_s32;
30802 *pChannels = pDataFormat->numChannels;
30803 *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000;
30804 ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap);
30809 static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
30812 SLDataLocator_AndroidSimpleBufferQueue queue;
30814 size_t bufferSizeInBytes;
30815 SLInterfaceID itfIDs1[1];
30816 const SLboolean itfIDsRequired1[] = {SL_BOOLEAN_TRUE};
30819 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. */
30820 if (g_maOpenSLInitCounter == 0) {
30821 return MA_INVALID_OPERATION;
30824 if (pConfig->deviceType == ma_device_type_loopback) {
30825 return MA_DEVICE_TYPE_NOT_SUPPORTED;
30829 For now, only supporting Android implementations of OpenSL|ES since that's the only one I've
30830 been able to test with and I currently depend on Android-specific extensions (simple buffer
30834 itfIDs1[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
30836 /* No exclusive mode with OpenSL|ES. */
30837 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
30838 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
30839 return MA_SHARE_MODE_NOT_SUPPORTED;
30842 /* Now we can start initializing the device properly. */
30843 MA_ASSERT(pDevice != NULL);
30844 MA_ZERO_OBJECT(&pDevice->opensl);
30846 queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
30848 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
30849 ma_SLDataFormat_PCM pcm;
30850 SLDataLocator_IODevice locatorDevice;
30851 SLDataSource source;
30853 SLAndroidConfigurationItf pRecorderConfig;
30855 ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm);
30857 locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE;
30858 locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT;
30859 locatorDevice.deviceID = (pDescriptorCapture->pDeviceID == NULL) ? SL_DEFAULTDEVICEID_AUDIOINPUT : pDescriptorCapture->pDeviceID->opensl;
30860 locatorDevice.device = NULL;
30862 source.pLocator = &locatorDevice;
30863 source.pFormat = NULL;
30865 queue.numBuffers = pDescriptorCapture->periodCount;
30867 sink.pLocator = &queue;
30868 sink.pFormat = (SLDataFormat_PCM*)&pcm;
30870 resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
30871 if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) {
30872 /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
30873 pcm.formatType = SL_DATAFORMAT_PCM;
30874 pcm.numChannels = 1;
30875 ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */
30876 pcm.bitsPerSample = 16;
30877 pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
30878 pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
30879 resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
30882 if (resultSL != SL_RESULT_SUCCESS) {
30883 ma_device_uninit__opensl(pDevice);
30884 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder.", ma_result_from_OpenSL(resultSL));
30888 /* Set the recording preset before realizing the player. */
30889 if (pConfig->opensl.recordingPreset != ma_opensl_recording_preset_default) {
30890 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig);
30891 if (resultSL == SL_RESULT_SUCCESS) {
30892 SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset);
30893 resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32));
30894 if (resultSL != SL_RESULT_SUCCESS) {
30895 /* Failed to set the configuration. Just keep going. */
30900 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE);
30901 if (resultSL != SL_RESULT_SUCCESS) {
30902 ma_device_uninit__opensl(pDevice);
30903 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder.", ma_result_from_OpenSL(resultSL));
30906 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder);
30907 if (resultSL != SL_RESULT_SUCCESS) {
30908 ma_device_uninit__opensl(pDevice);
30909 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.", ma_result_from_OpenSL(resultSL));
30912 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture);
30913 if (resultSL != SL_RESULT_SUCCESS) {
30914 ma_device_uninit__opensl(pDevice);
30915 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", ma_result_from_OpenSL(resultSL));
30918 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice);
30919 if (resultSL != SL_RESULT_SUCCESS) {
30920 ma_device_uninit__opensl(pDevice);
30921 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", ma_result_from_OpenSL(resultSL));
30924 /* The internal format is determined by the "pcm" object. */
30925 ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap));
30928 pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
30929 pDevice->opensl.currentBufferIndexCapture = 0;
30931 bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount;
30932 pDevice->opensl.pBufferCapture = (ma_uint8*)ma__calloc_from_callbacks(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks);
30933 if (pDevice->opensl.pBufferCapture == NULL) {
30934 ma_device_uninit__opensl(pDevice);
30935 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.", MA_OUT_OF_MEMORY);
30937 MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes);
30940 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
30941 ma_SLDataFormat_PCM pcm;
30942 SLDataSource source;
30943 SLDataLocator_OutputMix outmixLocator;
30945 SLAndroidConfigurationItf pPlayerConfig;
30947 ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm);
30949 resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL);
30950 if (resultSL != SL_RESULT_SUCCESS) {
30951 ma_device_uninit__opensl(pDevice);
30952 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix.", ma_result_from_OpenSL(resultSL));
30955 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE);
30956 if (resultSL != SL_RESULT_SUCCESS) {
30957 ma_device_uninit__opensl(pDevice);
30958 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object.", ma_result_from_OpenSL(resultSL));
30961 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix);
30962 if (resultSL != SL_RESULT_SUCCESS) {
30963 ma_device_uninit__opensl(pDevice);
30964 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.", ma_result_from_OpenSL(resultSL));
30967 /* Set the output device. */
30968 if (pDescriptorPlayback->pDeviceID != NULL) {
30969 SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl;
30970 MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL);
30973 queue.numBuffers = pDescriptorPlayback->periodCount;
30975 source.pLocator = &queue;
30976 source.pFormat = (SLDataFormat_PCM*)&pcm;
30978 outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;
30979 outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj;
30981 sink.pLocator = &outmixLocator;
30982 sink.pFormat = NULL;
30984 resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
30985 if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) {
30986 /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
30987 pcm.formatType = SL_DATAFORMAT_PCM;
30988 pcm.numChannels = 2;
30989 ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16;
30990 pcm.bitsPerSample = 16;
30991 pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
30992 pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
30993 resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
30996 if (resultSL != SL_RESULT_SUCCESS) {
30997 ma_device_uninit__opensl(pDevice);
30998 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player.", ma_result_from_OpenSL(resultSL));
31002 /* Set the stream type before realizing the player. */
31003 if (pConfig->opensl.streamType != ma_opensl_stream_type_default) {
31004 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig);
31005 if (resultSL == SL_RESULT_SUCCESS) {
31006 SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType);
31007 resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
31008 if (resultSL != SL_RESULT_SUCCESS) {
31009 /* Failed to set the configuration. Just keep going. */
31014 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE);
31015 if (resultSL != SL_RESULT_SUCCESS) {
31016 ma_device_uninit__opensl(pDevice);
31017 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player.", ma_result_from_OpenSL(resultSL));
31020 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer);
31021 if (resultSL != SL_RESULT_SUCCESS) {
31022 ma_device_uninit__opensl(pDevice);
31023 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.", ma_result_from_OpenSL(resultSL));
31026 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback);
31027 if (resultSL != SL_RESULT_SUCCESS) {
31028 ma_device_uninit__opensl(pDevice);
31029 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", ma_result_from_OpenSL(resultSL));
31032 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice);
31033 if (resultSL != SL_RESULT_SUCCESS) {
31034 ma_device_uninit__opensl(pDevice);
31035 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", ma_result_from_OpenSL(resultSL));
31038 /* The internal format is determined by the "pcm" object. */
31039 ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap));
31042 pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
31043 pDevice->opensl.currentBufferIndexPlayback = 0;
31045 bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount;
31046 pDevice->opensl.pBufferPlayback = (ma_uint8*)ma__calloc_from_callbacks(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks);
31047 if (pDevice->opensl.pBufferPlayback == NULL) {
31048 ma_device_uninit__opensl(pDevice);
31049 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.", MA_OUT_OF_MEMORY);
31051 MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes);
31056 return MA_NO_BACKEND; /* Non-Android implementations are not supported. */
31060 static ma_result ma_device_start__opensl(ma_device* pDevice)
31063 size_t periodSizeInBytes;
31066 MA_ASSERT(pDevice != NULL);
31068 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. */
31069 if (g_maOpenSLInitCounter == 0) {
31070 return MA_INVALID_OPERATION;
31073 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
31074 resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING);
31075 if (resultSL != SL_RESULT_SUCCESS) {
31076 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device.", ma_result_from_OpenSL(resultSL));
31079 periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
31080 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
31081 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes);
31082 if (resultSL != SL_RESULT_SUCCESS) {
31083 MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
31084 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device.", ma_result_from_OpenSL(resultSL));
31089 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
31090 resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING);
31091 if (resultSL != SL_RESULT_SUCCESS) {
31092 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device.", ma_result_from_OpenSL(resultSL));
31095 /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueu silent buffers. */
31096 if (pDevice->type == ma_device_type_duplex) {
31097 MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
31099 ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback);
31102 periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
31103 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
31104 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes);
31105 if (resultSL != SL_RESULT_SUCCESS) {
31106 MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
31107 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device.", ma_result_from_OpenSL(resultSL));
31115 static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType)
31117 SLAndroidSimpleBufferQueueItf pBufferQueue;
31119 MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback);
31121 if (pDevice->type == ma_device_type_capture) {
31122 pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture;
31123 pDevice->opensl.isDrainingCapture = MA_TRUE;
31125 pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback;
31126 pDevice->opensl.isDrainingPlayback = MA_TRUE;
31130 SLAndroidSimpleBufferQueueState state;
31132 MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state);
31133 if (state.count == 0) {
31140 if (pDevice->type == ma_device_type_capture) {
31141 pDevice->opensl.isDrainingCapture = MA_FALSE;
31143 pDevice->opensl.isDrainingPlayback = MA_FALSE;
31149 static ma_result ma_device_stop__opensl(ma_device* pDevice)
31152 ma_stop_proc onStop;
31154 MA_ASSERT(pDevice != NULL);
31156 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. */
31157 if (g_maOpenSLInitCounter == 0) {
31158 return MA_INVALID_OPERATION;
31161 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
31162 ma_device_drain__opensl(pDevice, ma_device_type_capture);
31164 resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
31165 if (resultSL != SL_RESULT_SUCCESS) {
31166 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device.", ma_result_from_OpenSL(resultSL));
31169 MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture);
31172 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
31173 ma_device_drain__opensl(pDevice, ma_device_type_playback);
31175 resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
31176 if (resultSL != SL_RESULT_SUCCESS) {
31177 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device.", ma_result_from_OpenSL(resultSL));
31180 MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback);
31183 /* 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. */
31184 onStop = pDevice->onStop;
31193 static ma_result ma_context_uninit__opensl(ma_context* pContext)
31195 MA_ASSERT(pContext != NULL);
31196 MA_ASSERT(pContext->backend == ma_backend_opensl);
31199 /* Uninit global data. */
31200 ma_spinlock_lock(&g_maOpenSLSpinlock);
31202 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(). */
31204 g_maOpenSLInitCounter -= 1;
31205 if (g_maOpenSLInitCounter == 0) {
31206 (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
31209 ma_spinlock_unlock(&g_maOpenSLSpinlock);
31214 static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle)
31216 /* 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. */
31217 ma_handle* p = (ma_handle*)ma_dlsym(pContext, pContext->opensl.libOpenSLES, pName);
31219 ma_post_log_messagef(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Cannot find symbol %s", pName);
31220 return MA_NO_BACKEND;
31227 static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext)
31229 g_maOpenSLInitCounter += 1;
31230 if (g_maOpenSLInitCounter == 1) {
31233 resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL);
31234 if (resultSL != SL_RESULT_SUCCESS) {
31235 g_maOpenSLInitCounter -= 1;
31236 return ma_result_from_OpenSL(resultSL);
31239 (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE);
31241 resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL);
31242 if (resultSL != SL_RESULT_SUCCESS) {
31243 (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
31244 g_maOpenSLInitCounter -= 1;
31245 return ma_result_from_OpenSL(resultSL);
31252 static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
31256 #if !defined(MA_NO_RUNTIME_LINKING)
31258 const char* libOpenSLESNames[] = {
31263 MA_ASSERT(pContext != NULL);
31267 #if !defined(MA_NO_RUNTIME_LINKING)
31269 Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One
31270 report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime
31271 and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any
31272 references to the symbols and will hopefully skip the checks.
31274 for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) {
31275 pContext->opensl.libOpenSLES = ma_dlopen(pContext, libOpenSLESNames[i]);
31276 if (pContext->opensl.libOpenSLES != NULL) {
31281 if (pContext->opensl.libOpenSLES == NULL) {
31282 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Could not find libOpenSLES.so");
31283 return MA_NO_BACKEND;
31286 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE);
31287 if (result != MA_SUCCESS) {
31288 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
31292 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES);
31293 if (result != MA_SUCCESS) {
31294 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
31298 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE);
31299 if (result != MA_SUCCESS) {
31300 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
31304 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD);
31305 if (result != MA_SUCCESS) {
31306 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
31310 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY);
31311 if (result != MA_SUCCESS) {
31312 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
31316 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX);
31317 if (result != MA_SUCCESS) {
31318 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
31322 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION);
31323 if (result != MA_SUCCESS) {
31324 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
31328 pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(pContext, pContext->opensl.libOpenSLES, "slCreateEngine");
31329 if (pContext->opensl.slCreateEngine == NULL) {
31330 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
31331 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Cannot find symbol slCreateEngine.");
31332 return MA_NO_BACKEND;
31335 pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE;
31336 pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES;
31337 pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
31338 pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD;
31339 pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY;
31340 pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX;
31341 pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION;
31342 pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine;
31346 /* Initialize global data first if applicable. */
31347 ma_spinlock_lock(&g_maOpenSLSpinlock);
31349 result = ma_context_init_engine_nolock__opensl(pContext);
31351 ma_spinlock_unlock(&g_maOpenSLSpinlock);
31353 if (result != MA_SUCCESS) {
31354 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
31355 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Failed to initialize OpenSL engine.");
31359 pCallbacks->onContextInit = ma_context_init__opensl;
31360 pCallbacks->onContextUninit = ma_context_uninit__opensl;
31361 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl;
31362 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl;
31363 pCallbacks->onDeviceInit = ma_device_init__opensl;
31364 pCallbacks->onDeviceUninit = ma_device_uninit__opensl;
31365 pCallbacks->onDeviceStart = ma_device_start__opensl;
31366 pCallbacks->onDeviceStop = ma_device_stop__opensl;
31367 pCallbacks->onDeviceRead = NULL; /* Not needed because OpenSL|ES is asynchronous. */
31368 pCallbacks->onDeviceWrite = NULL; /* Not needed because OpenSL|ES is asynchronous. */
31369 pCallbacks->onDeviceDataLoop = NULL; /* Not needed because OpenSL|ES is asynchronous. */
31373 #endif /* OpenSL|ES */
31376 /******************************************************************************
31380 ******************************************************************************/
31381 #ifdef MA_HAS_WEBAUDIO
31382 #include <emscripten/emscripten.h>
31384 static ma_bool32 ma_is_capture_supported__webaudio()
31386 return EM_ASM_INT({
31387 return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined);
31388 }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */
31394 void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
31396 ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount);
31399 void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
31401 ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount);
31407 static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
31409 ma_bool32 cbResult = MA_TRUE;
31411 MA_ASSERT(pContext != NULL);
31412 MA_ASSERT(callback != NULL);
31414 /* Only supporting default devices for now. */
31418 ma_device_info deviceInfo;
31419 MA_ZERO_OBJECT(&deviceInfo);
31420 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
31421 deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */
31422 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
31427 if (ma_is_capture_supported__webaudio()) {
31428 ma_device_info deviceInfo;
31429 MA_ZERO_OBJECT(&deviceInfo);
31430 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
31431 deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */
31432 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
31439 static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
31441 MA_ASSERT(pContext != NULL);
31443 if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
31444 return MA_NO_DEVICE;
31447 MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio));
31449 /* Only supporting default devices for now. */
31451 if (deviceType == ma_device_type_playback) {
31452 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
31454 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
31457 /* Only supporting default devices. */
31458 pDeviceInfo->isDefault = MA_TRUE;
31460 /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */
31461 pDeviceInfo->nativeDataFormats[0].flags = 0;
31462 pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown;
31463 pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channels are supported. */
31464 pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({
31466 var temp = new (window.AudioContext || window.webkitAudioContext)();
31467 var sampleRate = temp.sampleRate;
31473 }, 0); /* Must pass in a dummy argument for C99 compatibility. */
31475 if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) {
31476 return MA_NO_DEVICE;
31479 pDeviceInfo->nativeDataFormatCount = 1;
31485 static void ma_device_uninit_by_index__webaudio(ma_device* pDevice, ma_device_type deviceType, int deviceIndex)
31487 MA_ASSERT(pDevice != NULL);
31490 var device = miniaudio.get_device_by_index($0);
31492 /* Make sure all nodes are disconnected and marked for collection. */
31493 if (device.scriptNode !== undefined) {
31494 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... */
31495 device.scriptNode.disconnect();
31496 device.scriptNode = undefined;
31498 if (device.streamNode !== undefined) {
31499 device.streamNode.disconnect();
31500 device.streamNode = undefined;
31504 Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want
31505 to clear the callback before closing.
31507 device.webaudio.close();
31508 device.webaudio = undefined;
31510 /* Can't forget to free the intermediary buffer. This is the buffer that's shared between JavaScript and C. */
31511 if (device.intermediaryBuffer !== undefined) {
31512 Module._free(device.intermediaryBuffer);
31513 device.intermediaryBuffer = undefined;
31514 device.intermediaryBufferView = undefined;
31515 device.intermediaryBufferSizeInBytes = undefined;
31518 /* Make sure the device is untracked so the slot can be reused later. */
31519 miniaudio.untrack_device_by_index($0);
31520 }, deviceIndex, deviceType);
31523 static ma_result ma_device_uninit__webaudio(ma_device* pDevice)
31525 MA_ASSERT(pDevice != NULL);
31527 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
31528 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
31531 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
31532 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback);
31538 static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
31541 There have been reports of the default buffer size being too small on some browsers. There have been reports of the default buffer
31542 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
31545 ma_uint32 periodSizeInFrames;
31547 if (pDescriptor->periodSizeInFrames == 0) {
31548 if (pDescriptor->periodSizeInMilliseconds == 0) {
31549 if (performanceProfile == ma_performance_profile_low_latency) {
31550 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate); /* 1 frame @ 30 FPS */
31552 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate);
31555 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);
31558 periodSizeInFrames = pDescriptor->periodSizeInFrames;
31561 /* The size of the buffer must be a power of 2 and between 256 and 16384. */
31562 if (periodSizeInFrames < 256) {
31563 periodSizeInFrames = 256;
31564 } else if (periodSizeInFrames > 16384) {
31565 periodSizeInFrames = 16384;
31567 periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames);
31570 return periodSizeInFrames;
31573 static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
31576 ma_uint32 channels;
31577 ma_uint32 sampleRate;
31578 ma_uint32 periodSizeInFrames;
31580 MA_ASSERT(pDevice != NULL);
31581 MA_ASSERT(pConfig != NULL);
31582 MA_ASSERT(deviceType != ma_device_type_duplex);
31584 if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
31585 return MA_NO_DEVICE;
31588 /* We're going to calculate some stuff in C just to simplify the JS code. */
31589 channels = (pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS;
31590 sampleRate = (pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE;
31591 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptor, sampleRate, pConfig->performanceProfile);
31594 /* 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. */
31595 deviceIndex = EM_ASM_INT({
31597 var sampleRate = $1;
31598 var bufferSize = $2; /* In PCM frames. */
31599 var isCapture = $3;
31602 if (typeof(miniaudio) === 'undefined') {
31603 return -1; /* Context not initialized. */
31608 /* The AudioContext must be created in a suspended state. */
31609 device.webaudio = new (window.AudioContext || window.webkitAudioContext)({sampleRate:sampleRate});
31610 device.webaudio.suspend();
31611 device.state = 1; /* MA_STATE_STOPPED */
31614 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
31615 JavaScript and C it needs to be allocated and freed using Module._malloc() and Module._free().
31617 device.intermediaryBufferSizeInBytes = channels * bufferSize * 4;
31618 device.intermediaryBuffer = Module._malloc(device.intermediaryBufferSizeInBytes);
31619 device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
31622 Both playback and capture devices use a ScriptProcessorNode for performing per-sample operations.
31624 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
31625 that's periodically fired, just like a normal audio callback function. But apparently this design is "flawed" and is now deprecated in favour of
31626 something called AudioWorklets which _forces_ you to load a _separate_ .js file at run time... nice... Hopefully ScriptProcessorNode will continue to
31627 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
31628 implementation. I'll be avoiding that insane AudioWorklet API like the plague...
31630 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
31631 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
31632 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
31633 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
31634 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
31635 this for now. If anyone knows how I could get raw PCM data using the MediaRecorder API please let me know!
31637 device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channels, channels);
31640 device.scriptNode.onaudioprocess = function(e) {
31641 if (device.intermediaryBuffer === undefined) {
31642 return; /* This means the device has been uninitialized. */
31645 if(device.intermediaryBufferView.length == 0) {
31646 /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */
31647 device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
31650 /* Make sure silence it output to the AudioContext destination. Not doing this will cause sound to come out of the speakers! */
31651 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
31652 e.outputBuffer.getChannelData(iChannel).fill(0.0);
31655 /* There are some situations where we may want to send silence to the client. */
31656 var sendSilence = false;
31657 if (device.streamNode === undefined) {
31658 sendSilence = true;
31661 /* Sanity check. This will never happen, right? */
31662 if (e.inputBuffer.numberOfChannels != channels) {
31663 console.log("Capture: Channel count mismatch. " + e.inputBufer.numberOfChannels + " != " + channels + ". Sending silence.");
31664 sendSilence = true;
31667 /* This looped design guards against the situation where e.inputBuffer is a different size to the original buffer size. Should never happen in practice. */
31668 var totalFramesProcessed = 0;
31669 while (totalFramesProcessed < e.inputBuffer.length) {
31670 var framesRemaining = e.inputBuffer.length - totalFramesProcessed;
31671 var framesToProcess = framesRemaining;
31672 if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
31673 framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
31676 /* 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. */
31678 device.intermediaryBufferView.fill(0.0);
31680 for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
31681 for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) {
31682 device.intermediaryBufferView[iFrame*channels + iChannel] = e.inputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame];
31687 /* Send data to the client from our intermediary buffer. */
31688 ccall("ma_device_process_pcm_frames_capture__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]);
31690 totalFramesProcessed += framesToProcess;
31694 navigator.mediaDevices.getUserMedia({audio:true, video:false})
31695 .then(function(stream) {
31696 device.streamNode = device.webaudio.createMediaStreamSource(stream);
31697 device.streamNode.connect(device.scriptNode);
31698 device.scriptNode.connect(device.webaudio.destination);
31700 .catch(function(error) {
31701 /* I think this should output silence... */
31702 device.scriptNode.connect(device.webaudio.destination);
31705 device.scriptNode.onaudioprocess = function(e) {
31706 if (device.intermediaryBuffer === undefined) {
31707 return; /* This means the device has been uninitialized. */
31710 if(device.intermediaryBufferView.length == 0) {
31711 /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */
31712 device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
31715 var outputSilence = false;
31717 /* Sanity check. This will never happen, right? */
31718 if (e.outputBuffer.numberOfChannels != channels) {
31719 console.log("Playback: Channel count mismatch. " + e.outputBufer.numberOfChannels + " != " + channels + ". Outputting silence.");
31720 outputSilence = true;
31724 /* This looped design guards against the situation where e.outputBuffer is a different size to the original buffer size. Should never happen in practice. */
31725 var totalFramesProcessed = 0;
31726 while (totalFramesProcessed < e.outputBuffer.length) {
31727 var framesRemaining = e.outputBuffer.length - totalFramesProcessed;
31728 var framesToProcess = framesRemaining;
31729 if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
31730 framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
31733 /* Read data from the client into our intermediary buffer. */
31734 ccall("ma_device_process_pcm_frames_playback__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]);
31736 /* At this point we'll have data in our intermediary buffer which we now need to deinterleave and copy over to the output buffers. */
31737 if (outputSilence) {
31738 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
31739 e.outputBuffer.getChannelData(iChannel).fill(0.0);
31742 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
31743 for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
31744 e.outputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame] = device.intermediaryBufferView[iFrame*channels + iChannel];
31749 totalFramesProcessed += framesToProcess;
31753 device.scriptNode.connect(device.webaudio.destination);
31756 return miniaudio.track_device(device);
31757 }, channels, sampleRate, periodSizeInFrames, deviceType == ma_device_type_capture, pDevice);
31759 if (deviceIndex < 0) {
31760 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
31763 if (deviceType == ma_device_type_capture) {
31764 pDevice->webaudio.indexCapture = deviceIndex;
31766 pDevice->webaudio.indexPlayback = deviceIndex;
31769 pDescriptor->format = ma_format_f32;
31770 pDescriptor->channels = channels;
31771 ma_get_standard_channel_map(ma_standard_channel_map_webaudio, pDescriptor->channels, pDescriptor->channelMap);
31772 pDescriptor->sampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
31773 pDescriptor->periodSizeInFrames = periodSizeInFrames;
31774 pDescriptor->periodCount = 1;
31779 static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
31783 if (pConfig->deviceType == ma_device_type_loopback) {
31784 return MA_DEVICE_TYPE_NOT_SUPPORTED;
31787 /* No exclusive mode with Web Audio. */
31788 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
31789 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
31790 return MA_SHARE_MODE_NOT_SUPPORTED;
31793 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
31794 result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
31795 if (result != MA_SUCCESS) {
31800 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
31801 result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
31802 if (result != MA_SUCCESS) {
31803 if (pConfig->deviceType == ma_device_type_duplex) {
31804 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
31813 static ma_result ma_device_start__webaudio(ma_device* pDevice)
31815 MA_ASSERT(pDevice != NULL);
31817 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
31819 var device = miniaudio.get_device_by_index($0);
31820 device.webaudio.resume();
31821 device.state = 2; /* MA_STATE_STARTED */
31822 }, pDevice->webaudio.indexCapture);
31825 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
31827 var device = miniaudio.get_device_by_index($0);
31828 device.webaudio.resume();
31829 device.state = 2; /* MA_STATE_STARTED */
31830 }, pDevice->webaudio.indexPlayback);
31836 static ma_result ma_device_stop__webaudio(ma_device* pDevice)
31838 MA_ASSERT(pDevice != NULL);
31841 From the WebAudio API documentation for AudioContext.suspend():
31843 Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the
31844 destination, and then allows the system to release its claim on audio hardware.
31846 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
31847 do any kind of explicit draining.
31850 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
31852 var device = miniaudio.get_device_by_index($0);
31853 device.webaudio.suspend();
31854 device.state = 1; /* MA_STATE_STOPPED */
31855 }, pDevice->webaudio.indexCapture);
31858 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
31860 var device = miniaudio.get_device_by_index($0);
31861 device.webaudio.suspend();
31862 device.state = 1; /* MA_STATE_STOPPED */
31863 }, pDevice->webaudio.indexPlayback);
31866 ma_stop_proc onStop = pDevice->onStop;
31874 static ma_result ma_context_uninit__webaudio(ma_context* pContext)
31876 MA_ASSERT(pContext != NULL);
31877 MA_ASSERT(pContext->backend == ma_backend_webaudio);
31879 /* Nothing needs to be done here. */
31885 static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
31889 MA_ASSERT(pContext != NULL);
31891 (void)pConfig; /* Unused. */
31893 /* Here is where our global JavaScript object is initialized. */
31894 resultFromJS = EM_ASM_INT({
31895 if ((window.AudioContext || window.webkitAudioContext) === undefined) {
31896 return 0; /* Web Audio not supported. */
31899 if (typeof(miniaudio) === 'undefined') {
31901 miniaudio.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */
31903 miniaudio.track_device = function(device) {
31904 /* Try inserting into a free slot first. */
31905 for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
31906 if (miniaudio.devices[iDevice] == null) {
31907 miniaudio.devices[iDevice] = device;
31912 /* Getting here means there is no empty slots in the array so we just push to the end. */
31913 miniaudio.devices.push(device);
31914 return miniaudio.devices.length - 1;
31917 miniaudio.untrack_device_by_index = function(deviceIndex) {
31918 /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */
31919 miniaudio.devices[deviceIndex] = null;
31921 /* Trim the array if possible. */
31922 while (miniaudio.devices.length > 0) {
31923 if (miniaudio.devices[miniaudio.devices.length-1] == null) {
31924 miniaudio.devices.pop();
31931 miniaudio.untrack_device = function(device) {
31932 for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
31933 if (miniaudio.devices[iDevice] == device) {
31934 return miniaudio.untrack_device_by_index(iDevice);
31939 miniaudio.get_device_by_index = function(deviceIndex) {
31940 return miniaudio.devices[deviceIndex];
31943 miniaudio.unlock_event_types = (function(){
31944 return ['touchstart', 'touchend', 'click'];
31947 miniaudio.unlock = function() {
31948 for(var i = 0; i < miniaudio.devices.length; ++i) {
31949 var device = miniaudio.devices[i];
31950 if (device != null && device.webaudio != null && device.state === 2 /* MA_STATE_STARTED */) {
31951 device.webaudio.resume();
31954 miniaudio.unlock_event_types.map(function(event_type) {
31955 document.removeEventListener(event_type, miniaudio.unlock, true);
31959 miniaudio.unlock_event_types.map(function(event_type) {
31960 document.addEventListener(event_type, miniaudio.unlock, true);
31965 }, 0); /* Must pass in a dummy argument for C99 compatibility. */
31967 if (resultFromJS != 1) {
31968 return MA_FAILED_TO_INIT_BACKEND;
31971 pCallbacks->onContextInit = ma_context_init__webaudio;
31972 pCallbacks->onContextUninit = ma_context_uninit__webaudio;
31973 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio;
31974 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio;
31975 pCallbacks->onDeviceInit = ma_device_init__webaudio;
31976 pCallbacks->onDeviceUninit = ma_device_uninit__webaudio;
31977 pCallbacks->onDeviceStart = ma_device_start__webaudio;
31978 pCallbacks->onDeviceStop = ma_device_stop__webaudio;
31979 pCallbacks->onDeviceRead = NULL; /* Not needed because WebAudio is asynchronous. */
31980 pCallbacks->onDeviceWrite = NULL; /* Not needed because WebAudio is asynchronous. */
31981 pCallbacks->onDeviceDataLoop = NULL; /* Not needed because WebAudio is asynchronous. */
31985 #endif /* Web Audio */
31989 static ma_bool32 ma__is_channel_map_valid(const ma_channel* channelMap, ma_uint32 channels)
31991 /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */
31992 if (channelMap[0] != MA_CHANNEL_NONE) {
31993 ma_uint32 iChannel;
31995 if (channels == 0 || channels > MA_MAX_CHANNELS) {
31996 return MA_FALSE; /* Channel count out of range. */
31999 /* A channel cannot be present in the channel map more than once. */
32000 for (iChannel = 0; iChannel < channels; ++iChannel) {
32001 ma_uint32 jChannel;
32002 for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) {
32003 if (channelMap[iChannel] == channelMap[jChannel]) {
32014 static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType)
32018 MA_ASSERT(pDevice != NULL);
32020 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
32021 if (pDevice->capture.format == ma_format_unknown) {
32022 pDevice->capture.format = pDevice->capture.internalFormat;
32024 if (pDevice->capture.channels == 0) {
32025 pDevice->capture.channels = pDevice->capture.internalChannels;
32027 if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) {
32028 MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS);
32029 if (pDevice->capture.internalChannels == pDevice->capture.channels) {
32030 ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels);
32032 if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) {
32033 ma_channel_map_init_blank(pDevice->capture.channels, pDevice->capture.channelMap);
32035 ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->capture.channels, pDevice->capture.channelMap);
32041 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
32042 if (pDevice->playback.format == ma_format_unknown) {
32043 pDevice->playback.format = pDevice->playback.internalFormat;
32045 if (pDevice->playback.channels == 0) {
32046 pDevice->playback.channels = pDevice->playback.internalChannels;
32048 if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) {
32049 MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS);
32050 if (pDevice->playback.internalChannels == pDevice->playback.channels) {
32051 ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels);
32053 if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) {
32054 ma_channel_map_init_blank(pDevice->playback.channels, pDevice->playback.channelMap);
32056 ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->playback.channels, pDevice->playback.channelMap);
32062 if (pDevice->sampleRate == 0) {
32063 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
32064 pDevice->sampleRate = pDevice->capture.internalSampleRate;
32066 pDevice->sampleRate = pDevice->playback.internalSampleRate;
32070 /* Data converters. */
32071 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
32072 /* Converting from internal device format to client format. */
32073 ma_data_converter_config converterConfig = ma_data_converter_config_init_default();
32074 converterConfig.formatIn = pDevice->capture.internalFormat;
32075 converterConfig.channelsIn = pDevice->capture.internalChannels;
32076 converterConfig.sampleRateIn = pDevice->capture.internalSampleRate;
32077 ma_channel_map_copy(converterConfig.channelMapIn, pDevice->capture.internalChannelMap, ma_min(pDevice->capture.internalChannels, MA_MAX_CHANNELS));
32078 converterConfig.formatOut = pDevice->capture.format;
32079 converterConfig.channelsOut = pDevice->capture.channels;
32080 converterConfig.sampleRateOut = pDevice->sampleRate;
32081 ma_channel_map_copy(converterConfig.channelMapOut, pDevice->capture.channelMap, ma_min(pDevice->capture.channels, MA_MAX_CHANNELS));
32082 converterConfig.channelMixMode = pDevice->capture.channelMixMode;
32083 converterConfig.resampling.allowDynamicSampleRate = MA_FALSE;
32084 converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
32085 converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
32086 converterConfig.resampling.speex.quality = pDevice->resampling.speex.quality;
32088 result = ma_data_converter_init(&converterConfig, &pDevice->capture.converter);
32089 if (result != MA_SUCCESS) {
32094 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
32095 /* Converting from client format to device format. */
32096 ma_data_converter_config converterConfig = ma_data_converter_config_init_default();
32097 converterConfig.formatIn = pDevice->playback.format;
32098 converterConfig.channelsIn = pDevice->playback.channels;
32099 converterConfig.sampleRateIn = pDevice->sampleRate;
32100 ma_channel_map_copy(converterConfig.channelMapIn, pDevice->playback.channelMap, ma_min(pDevice->playback.channels, MA_MAX_CHANNELS));
32101 converterConfig.formatOut = pDevice->playback.internalFormat;
32102 converterConfig.channelsOut = pDevice->playback.internalChannels;
32103 converterConfig.sampleRateOut = pDevice->playback.internalSampleRate;
32104 ma_channel_map_copy(converterConfig.channelMapOut, pDevice->playback.internalChannelMap, ma_min(pDevice->playback.internalChannels, MA_MAX_CHANNELS));
32105 converterConfig.channelMixMode = pDevice->playback.channelMixMode;
32106 converterConfig.resampling.allowDynamicSampleRate = MA_FALSE;
32107 converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
32108 converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
32109 converterConfig.resampling.speex.quality = pDevice->resampling.speex.quality;
32111 result = ma_data_converter_init(&converterConfig, &pDevice->playback.converter);
32112 if (result != MA_SUCCESS) {
32121 static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
32123 ma_device* pDevice = (ma_device*)pData;
32124 MA_ASSERT(pDevice != NULL);
32127 ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE);
32131 When the device is being initialized it's initial state is set to MA_STATE_UNINITIALIZED. Before returning from
32132 ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately
32133 after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker
32134 thread to signal an event to know when the worker thread is ready for action.
32136 ma_device__set_state(pDevice, MA_STATE_STOPPED);
32137 ma_event_signal(&pDevice->stopEvent);
32139 for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */
32140 ma_stop_proc onStop;
32142 /* We wait on an event to know when something has requested that the device be started and the main loop entered. */
32143 ma_event_wait(&pDevice->wakeupEvent);
32145 /* Default result code. */
32146 pDevice->workResult = MA_SUCCESS;
32148 /* If the reason for the wake up is that we are terminating, just break from the loop. */
32149 if (ma_device_get_state(pDevice) == MA_STATE_UNINITIALIZED) {
32154 Getting to this point means the device is wanting to get started. The function that has requested that the device
32155 be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event
32156 in both the success and error case. It's important that the state of the device is set _before_ signaling the event.
32158 MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_STARTING);
32160 /* If the device has a start callback, start it now. */
32161 if (pDevice->pContext->callbacks.onDeviceStart != NULL) {
32162 ma_result result = pDevice->pContext->callbacks.onDeviceStart(pDevice);
32163 if (result != MA_SUCCESS) {
32164 pDevice->workResult = result; /* Failed to start the device. */
32168 /* Make sure the state is set appropriately. */
32169 ma_device__set_state(pDevice, MA_STATE_STARTED);
32170 ma_event_signal(&pDevice->startEvent);
32172 if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) {
32173 pDevice->pContext->callbacks.onDeviceDataLoop(pDevice);
32175 /* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */
32176 ma_device_audio_thread__default_read_write(pDevice);
32180 Getting here means we have broken from the main loop which happens the application has requested that device be stopped. Note that this
32181 may have actually already happened above if the device was lost and miniaudio has attempted to re-initialize the device. In this case we
32182 don't want to be doing this a second time.
32184 if (ma_device_get_state(pDevice) != MA_STATE_UNINITIALIZED) {
32185 if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
32186 pDevice->pContext->callbacks.onDeviceStop(pDevice);
32190 /* After the device has stopped, make sure an event is posted. */
32191 onStop = pDevice->onStop;
32197 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. Note that
32198 it's possible that the device has been uninitialized which means we need to _not_ change the status to stopped. We cannot go from an
32199 uninitialized state to stopped state.
32201 if (ma_device_get_state(pDevice) != MA_STATE_UNINITIALIZED) {
32202 ma_device__set_state(pDevice, MA_STATE_STOPPED);
32203 ma_event_signal(&pDevice->stopEvent);
32207 /* Make sure we aren't continuously waiting on a stop event. */
32208 ma_event_signal(&pDevice->stopEvent); /* <-- Is this still needed? */
32211 ma_CoUninitialize(pDevice->pContext);
32214 return (ma_thread_result)0;
32218 /* Helper for determining whether or not the given device is initialized. */
32219 static ma_bool32 ma_device__is_initialized(ma_device* pDevice)
32221 if (pDevice == NULL) {
32225 return ma_device_get_state(pDevice) != MA_STATE_UNINITIALIZED;
32230 static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext)
32232 ma_CoUninitialize(pContext);
32233 ma_dlclose(pContext, pContext->win32.hUser32DLL);
32234 ma_dlclose(pContext, pContext->win32.hOle32DLL);
32235 ma_dlclose(pContext, pContext->win32.hAdvapi32DLL);
32240 static ma_result ma_context_init_backend_apis__win32(ma_context* pContext)
32242 #ifdef MA_WIN32_DESKTOP
32244 pContext->win32.hOle32DLL = ma_dlopen(pContext, "ole32.dll");
32245 if (pContext->win32.hOle32DLL == NULL) {
32246 return MA_FAILED_TO_INIT_BACKEND;
32249 pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitializeEx");
32250 pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoUninitialize");
32251 pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoCreateInstance");
32252 pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoTaskMemFree");
32253 pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "PropVariantClear");
32254 pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "StringFromGUID2");
32258 pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll");
32259 if (pContext->win32.hUser32DLL == NULL) {
32260 return MA_FAILED_TO_INIT_BACKEND;
32263 pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow");
32264 pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow");
32268 pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll");
32269 if (pContext->win32.hAdvapi32DLL == NULL) {
32270 return MA_FAILED_TO_INIT_BACKEND;
32273 pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA");
32274 pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey");
32275 pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA");
32278 ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE);
32282 static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext)
32284 #if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING)
32285 ma_dlclose(pContext, pContext->posix.pthreadSO);
32293 static ma_result ma_context_init_backend_apis__nix(ma_context* pContext)
32296 #if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING)
32297 const char* libpthreadFileNames[] = {
32304 for (i = 0; i < sizeof(libpthreadFileNames) / sizeof(libpthreadFileNames[0]); ++i) {
32305 pContext->posix.pthreadSO = ma_dlopen(pContext, libpthreadFileNames[i]);
32306 if (pContext->posix.pthreadSO != NULL) {
32311 if (pContext->posix.pthreadSO == NULL) {
32312 return MA_FAILED_TO_INIT_BACKEND;
32315 pContext->posix.pthread_create = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_create");
32316 pContext->posix.pthread_join = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_join");
32317 pContext->posix.pthread_mutex_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_init");
32318 pContext->posix.pthread_mutex_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_destroy");
32319 pContext->posix.pthread_mutex_lock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_lock");
32320 pContext->posix.pthread_mutex_unlock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_unlock");
32321 pContext->posix.pthread_cond_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_init");
32322 pContext->posix.pthread_cond_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_destroy");
32323 pContext->posix.pthread_cond_wait = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_wait");
32324 pContext->posix.pthread_cond_signal = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_signal");
32325 pContext->posix.pthread_attr_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_init");
32326 pContext->posix.pthread_attr_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_destroy");
32327 pContext->posix.pthread_attr_setschedpolicy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedpolicy");
32328 pContext->posix.pthread_attr_getschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_getschedparam");
32329 pContext->posix.pthread_attr_setschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedparam");
32331 pContext->posix.pthread_create = (ma_proc)pthread_create;
32332 pContext->posix.pthread_join = (ma_proc)pthread_join;
32333 pContext->posix.pthread_mutex_init = (ma_proc)pthread_mutex_init;
32334 pContext->posix.pthread_mutex_destroy = (ma_proc)pthread_mutex_destroy;
32335 pContext->posix.pthread_mutex_lock = (ma_proc)pthread_mutex_lock;
32336 pContext->posix.pthread_mutex_unlock = (ma_proc)pthread_mutex_unlock;
32337 pContext->posix.pthread_cond_init = (ma_proc)pthread_cond_init;
32338 pContext->posix.pthread_cond_destroy = (ma_proc)pthread_cond_destroy;
32339 pContext->posix.pthread_cond_wait = (ma_proc)pthread_cond_wait;
32340 pContext->posix.pthread_cond_signal = (ma_proc)pthread_cond_signal;
32341 pContext->posix.pthread_attr_init = (ma_proc)pthread_attr_init;
32342 pContext->posix.pthread_attr_destroy = (ma_proc)pthread_attr_destroy;
32343 #if !defined(__EMSCRIPTEN__)
32344 pContext->posix.pthread_attr_setschedpolicy = (ma_proc)pthread_attr_setschedpolicy;
32345 pContext->posix.pthread_attr_getschedparam = (ma_proc)pthread_attr_getschedparam;
32346 pContext->posix.pthread_attr_setschedparam = (ma_proc)pthread_attr_setschedparam;
32354 static ma_result ma_context_init_backend_apis(ma_context* pContext)
32358 result = ma_context_init_backend_apis__win32(pContext);
32360 result = ma_context_init_backend_apis__nix(pContext);
32366 static ma_result ma_context_uninit_backend_apis(ma_context* pContext)
32370 result = ma_context_uninit_backend_apis__win32(pContext);
32372 result = ma_context_uninit_backend_apis__nix(pContext);
32379 static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext)
32381 MA_ASSERT(pContext != NULL);
32383 if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) {
32384 if (pContext->callbacks.onDeviceDataLoop == NULL) {
32395 MA_API ma_context_config ma_context_config_init()
32397 ma_context_config config;
32398 MA_ZERO_OBJECT(&config);
32403 MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext)
32406 ma_context_config defaultConfig;
32407 ma_backend defaultBackends[ma_backend_null+1];
32408 ma_uint32 iBackend;
32409 ma_backend* pBackendsToIterate;
32410 ma_uint32 backendsToIterateCount;
32412 if (pContext == NULL) {
32413 return MA_INVALID_ARGS;
32416 MA_ZERO_OBJECT(pContext);
32418 /* Always make sure the config is set first to ensure properties are available as soon as possible. */
32419 if (pConfig == NULL) {
32420 defaultConfig = ma_context_config_init();
32421 pConfig = &defaultConfig;
32424 pContext->logCallback = pConfig->logCallback;
32425 pContext->threadPriority = pConfig->threadPriority;
32426 pContext->threadStackSize = pConfig->threadStackSize;
32427 pContext->pUserData = pConfig->pUserData;
32429 result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks);
32430 if (result != MA_SUCCESS) {
32434 /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */
32435 result = ma_context_init_backend_apis(pContext);
32436 if (result != MA_SUCCESS) {
32440 for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
32441 defaultBackends[iBackend] = (ma_backend)iBackend;
32444 pBackendsToIterate = (ma_backend*)backends;
32445 backendsToIterateCount = backendCount;
32446 if (pBackendsToIterate == NULL) {
32447 pBackendsToIterate = (ma_backend*)defaultBackends;
32448 backendsToIterateCount = ma_countof(defaultBackends);
32451 MA_ASSERT(pBackendsToIterate != NULL);
32453 for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) {
32454 ma_backend backend = pBackendsToIterate[iBackend];
32456 /* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */
32457 MA_ZERO_OBJECT(&pContext->callbacks);
32459 /* These backends are using the new callback system. */
32461 #ifdef MA_HAS_WASAPI
32462 case ma_backend_wasapi:
32464 pContext->callbacks.onContextInit = ma_context_init__wasapi;
32467 #ifdef MA_HAS_DSOUND
32468 case ma_backend_dsound:
32470 pContext->callbacks.onContextInit = ma_context_init__dsound;
32473 #ifdef MA_HAS_WINMM
32474 case ma_backend_winmm:
32476 pContext->callbacks.onContextInit = ma_context_init__winmm;
32479 #ifdef MA_HAS_COREAUDIO
32480 case ma_backend_coreaudio:
32482 pContext->callbacks.onContextInit = ma_context_init__coreaudio;
32485 #ifdef MA_HAS_SNDIO
32486 case ma_backend_sndio:
32488 pContext->callbacks.onContextInit = ma_context_init__sndio;
32491 #ifdef MA_HAS_AUDIO4
32492 case ma_backend_audio4:
32494 pContext->callbacks.onContextInit = ma_context_init__audio4;
32498 case ma_backend_oss:
32500 pContext->callbacks.onContextInit = ma_context_init__oss;
32503 #ifdef MA_HAS_PULSEAUDIO
32504 case ma_backend_pulseaudio:
32506 pContext->callbacks.onContextInit = ma_context_init__pulse;
32510 case ma_backend_alsa:
32512 pContext->callbacks.onContextInit = ma_context_init__alsa;
32516 case ma_backend_jack:
32518 pContext->callbacks.onContextInit = ma_context_init__jack;
32521 #ifdef MA_HAS_AAUDIO
32522 case ma_backend_aaudio:
32524 pContext->callbacks.onContextInit = ma_context_init__aaudio;
32527 #ifdef MA_HAS_OPENSL
32528 case ma_backend_opensl:
32530 pContext->callbacks.onContextInit = ma_context_init__opensl;
32533 #ifdef MA_HAS_WEBAUDIO
32534 case ma_backend_webaudio:
32536 pContext->callbacks.onContextInit = ma_context_init__webaudio;
32539 #ifdef MA_HAS_CUSTOM
32540 case ma_backend_custom:
32542 /* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */
32543 pContext->callbacks = pConfig->custom;
32547 case ma_backend_null:
32549 pContext->callbacks.onContextInit = ma_context_init__null;
32556 if (pContext->callbacks.onContextInit != NULL) {
32557 ma_post_log_messagef(pContext, NULL, MA_LOG_LEVEL_VERBOSE, "Attempting to initialize %s backend...", ma_get_backend_name(backend));
32558 result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks);
32560 result = MA_NO_BACKEND;
32563 /* If this iteration was successful, return. */
32564 if (result == MA_SUCCESS) {
32565 result = ma_mutex_init(&pContext->deviceEnumLock);
32566 if (result != MA_SUCCESS) {
32567 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.", result);
32570 result = ma_mutex_init(&pContext->deviceInfoLock);
32571 if (result != MA_SUCCESS) {
32572 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.", result);
32575 #ifdef MA_DEBUG_OUTPUT
32577 printf("[miniaudio] Endian: %s\n", ma_is_little_endian() ? "LE" : "BE");
32578 printf("[miniaudio] SSE2: %s\n", ma_has_sse2() ? "YES" : "NO");
32579 printf("[miniaudio] AVX2: %s\n", ma_has_avx2() ? "YES" : "NO");
32580 printf("[miniaudio] AVX512F: %s\n", ma_has_avx512f() ? "YES" : "NO");
32581 printf("[miniaudio] NEON: %s\n", ma_has_neon() ? "YES" : "NO");
32585 pContext->backend = backend;
32588 ma_post_log_messagef(pContext, NULL, MA_LOG_LEVEL_VERBOSE, "Failed to initialize %s backend.", ma_get_backend_name(backend));
32592 /* If we get here it means an error occurred. */
32593 MA_ZERO_OBJECT(pContext); /* Safety. */
32594 return MA_NO_BACKEND;
32597 MA_API ma_result ma_context_uninit(ma_context* pContext)
32599 if (pContext == NULL) {
32600 return MA_INVALID_ARGS;
32603 if (pContext->callbacks.onContextUninit != NULL) {
32604 pContext->callbacks.onContextUninit(pContext);
32607 ma_mutex_uninit(&pContext->deviceEnumLock);
32608 ma_mutex_uninit(&pContext->deviceInfoLock);
32609 ma__free_from_callbacks(pContext->pDeviceInfos, &pContext->allocationCallbacks);
32610 ma_context_uninit_backend_apis(pContext);
32615 MA_API size_t ma_context_sizeof()
32617 return sizeof(ma_context);
32621 MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
32625 if (pContext == NULL || callback == NULL) {
32626 return MA_INVALID_ARGS;
32629 if (pContext->callbacks.onContextEnumerateDevices == NULL) {
32630 return MA_INVALID_OPERATION;
32633 ma_mutex_lock(&pContext->deviceEnumLock);
32635 result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData);
32637 ma_mutex_unlock(&pContext->deviceEnumLock);
32643 static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
32646 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
32647 it's just appended to the end. If it's a playback device it's inserted just before the first capture device.
32651 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
32652 simple fixed size increment for buffer expansion.
32654 const ma_uint32 bufferExpansionCount = 2;
32655 const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount;
32657 if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) {
32658 ma_uint32 oldCapacity = pContext->deviceInfoCapacity;
32659 ma_uint32 newCapacity = oldCapacity + bufferExpansionCount;
32660 ma_device_info* pNewInfos = (ma_device_info*)ma__realloc_from_callbacks(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, sizeof(*pContext->pDeviceInfos)*oldCapacity, &pContext->allocationCallbacks);
32661 if (pNewInfos == NULL) {
32662 return MA_FALSE; /* Out of memory. */
32665 pContext->pDeviceInfos = pNewInfos;
32666 pContext->deviceInfoCapacity = newCapacity;
32669 if (deviceType == ma_device_type_playback) {
32670 /* Playback. Insert just before the first capture device. */
32672 /* The first thing to do is move all of the capture devices down a slot. */
32673 ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount;
32674 size_t iCaptureDevice;
32675 for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) {
32676 pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1];
32679 /* Now just insert where the first capture device was before moving it down a slot. */
32680 pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo;
32681 pContext->playbackDeviceInfoCount += 1;
32683 /* Capture. Insert at the end. */
32684 pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo;
32685 pContext->captureDeviceInfoCount += 1;
32692 MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount)
32697 if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL;
32698 if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0;
32699 if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL;
32700 if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0;
32702 if (pContext == NULL) {
32703 return MA_INVALID_ARGS;
32706 if (pContext->callbacks.onContextEnumerateDevices == NULL) {
32707 return MA_INVALID_OPERATION;
32710 /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */
32711 ma_mutex_lock(&pContext->deviceEnumLock);
32713 /* Reset everything first. */
32714 pContext->playbackDeviceInfoCount = 0;
32715 pContext->captureDeviceInfoCount = 0;
32717 /* Now enumerate over available devices. */
32718 result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL);
32719 if (result == MA_SUCCESS) {
32720 /* Playback devices. */
32721 if (ppPlaybackDeviceInfos != NULL) {
32722 *ppPlaybackDeviceInfos = pContext->pDeviceInfos;
32724 if (pPlaybackDeviceCount != NULL) {
32725 *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount;
32728 /* Capture devices. */
32729 if (ppCaptureDeviceInfos != NULL) {
32730 *ppCaptureDeviceInfos = pContext->pDeviceInfos + pContext->playbackDeviceInfoCount; /* Capture devices come after playback devices. */
32732 if (pCaptureDeviceCount != NULL) {
32733 *pCaptureDeviceCount = pContext->captureDeviceInfoCount;
32737 ma_mutex_unlock(&pContext->deviceEnumLock);
32742 MA_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)
32745 ma_device_info deviceInfo;
32747 (void)shareMode; /* Unused. This parameter will be removed in version 0.11. */
32749 /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */
32750 if (pContext == NULL || pDeviceInfo == NULL) {
32751 return MA_INVALID_ARGS;
32754 MA_ZERO_OBJECT(&deviceInfo);
32756 /* Help the backend out by copying over the device ID if we have one. */
32757 if (pDeviceID != NULL) {
32758 MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID));
32761 if (pContext->callbacks.onContextGetDeviceInfo == NULL) {
32762 return MA_INVALID_OPERATION;
32765 ma_mutex_lock(&pContext->deviceInfoLock);
32767 result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo);
32769 ma_mutex_unlock(&pContext->deviceInfoLock);
32772 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
32773 the future when all backends have implemented the new device info system.
32775 if (deviceInfo.nativeDataFormatCount > 0) {
32776 ma_uint32 iNativeFormat;
32777 ma_uint32 iSampleFormat;
32779 deviceInfo.minChannels = 0xFFFFFFFF;
32780 deviceInfo.maxChannels = 0;
32781 deviceInfo.minSampleRate = 0xFFFFFFFF;
32782 deviceInfo.maxSampleRate = 0;
32784 for (iNativeFormat = 0; iNativeFormat < deviceInfo.nativeDataFormatCount; iNativeFormat += 1) {
32786 if (deviceInfo.nativeDataFormats[iNativeFormat].format == ma_format_unknown) {
32787 /* All formats are supported. */
32788 deviceInfo.formats[0] = ma_format_u8;
32789 deviceInfo.formats[1] = ma_format_s16;
32790 deviceInfo.formats[2] = ma_format_s24;
32791 deviceInfo.formats[3] = ma_format_s32;
32792 deviceInfo.formats[4] = ma_format_f32;
32793 deviceInfo.formatCount = 5;
32795 /* Make sure the format isn't already in the list. If so, skip. */
32796 ma_bool32 alreadyExists = MA_FALSE;
32797 for (iSampleFormat = 0; iSampleFormat < deviceInfo.formatCount; iSampleFormat += 1) {
32798 if (deviceInfo.formats[iSampleFormat] == deviceInfo.nativeDataFormats[iNativeFormat].format) {
32799 alreadyExists = MA_TRUE;
32804 if (!alreadyExists) {
32805 deviceInfo.formats[deviceInfo.formatCount++] = deviceInfo.nativeDataFormats[iNativeFormat].format;
32810 if (deviceInfo.nativeDataFormats[iNativeFormat].channels == 0) {
32811 /* All channels supported. */
32812 deviceInfo.minChannels = MA_MIN_CHANNELS;
32813 deviceInfo.maxChannels = MA_MAX_CHANNELS;
32815 if (deviceInfo.minChannels > deviceInfo.nativeDataFormats[iNativeFormat].channels) {
32816 deviceInfo.minChannels = deviceInfo.nativeDataFormats[iNativeFormat].channels;
32818 if (deviceInfo.maxChannels < deviceInfo.nativeDataFormats[iNativeFormat].channels) {
32819 deviceInfo.maxChannels = deviceInfo.nativeDataFormats[iNativeFormat].channels;
32824 if (deviceInfo.nativeDataFormats[iNativeFormat].sampleRate == 0) {
32825 /* All sample rates supported. */
32826 deviceInfo.minSampleRate = (ma_uint32)ma_standard_sample_rate_min;
32827 deviceInfo.maxSampleRate = (ma_uint32)ma_standard_sample_rate_max;
32829 if (deviceInfo.minSampleRate > deviceInfo.nativeDataFormats[iNativeFormat].sampleRate) {
32830 deviceInfo.minSampleRate = deviceInfo.nativeDataFormats[iNativeFormat].sampleRate;
32832 if (deviceInfo.maxSampleRate < deviceInfo.nativeDataFormats[iNativeFormat].sampleRate) {
32833 deviceInfo.maxSampleRate = deviceInfo.nativeDataFormats[iNativeFormat].sampleRate;
32840 /* Clamp ranges. */
32841 deviceInfo.minChannels = ma_max(deviceInfo.minChannels, MA_MIN_CHANNELS);
32842 deviceInfo.maxChannels = ma_min(deviceInfo.maxChannels, MA_MAX_CHANNELS);
32843 deviceInfo.minSampleRate = ma_max(deviceInfo.minSampleRate, (ma_uint32)ma_standard_sample_rate_min);
32844 deviceInfo.maxSampleRate = ma_min(deviceInfo.maxSampleRate, (ma_uint32)ma_standard_sample_rate_max);
32846 *pDeviceInfo = deviceInfo;
32850 MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext)
32852 if (pContext == NULL) {
32856 return ma_is_loopback_supported(pContext->backend);
32860 MA_API ma_device_config ma_device_config_init(ma_device_type deviceType)
32862 ma_device_config config;
32863 MA_ZERO_OBJECT(&config);
32864 config.deviceType = deviceType;
32866 /* Resampling defaults. We must never use the Speex backend by default because it uses licensed third party code. */
32867 config.resampling.algorithm = ma_resample_algorithm_linear;
32868 config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
32869 config.resampling.speex.quality = 3;
32874 MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
32877 ma_device_descriptor descriptorPlayback;
32878 ma_device_descriptor descriptorCapture;
32880 /* The context can be null, in which case we self-manage it. */
32881 if (pContext == NULL) {
32882 return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice);
32885 if (pDevice == NULL) {
32886 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
32889 MA_ZERO_OBJECT(pDevice);
32891 if (pConfig == NULL) {
32892 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pConfig == NULL).", MA_INVALID_ARGS);
32896 /* Check that we have our callbacks defined. */
32897 if (pContext->callbacks.onDeviceInit == NULL) {
32898 return MA_INVALID_OPERATION;
32902 /* Basic config validation. */
32903 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) {
32904 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);
32907 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
32908 if (pConfig->capture.channels > MA_MAX_CHANNELS) {
32909 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);
32911 if (!ma__is_channel_map_valid(pConfig->capture.channelMap, pConfig->capture.channels)) {
32912 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);
32916 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
32917 if (pConfig->playback.channels > MA_MAX_CHANNELS) {
32918 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);
32920 if (!ma__is_channel_map_valid(pConfig->playback.channelMap, pConfig->playback.channels)) {
32921 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);
32925 pDevice->pContext = pContext;
32927 /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */
32928 pDevice->pUserData = pConfig->pUserData;
32929 pDevice->onData = pConfig->dataCallback;
32930 pDevice->onStop = pConfig->stopCallback;
32932 if (((ma_uintptr)pDevice % sizeof(pDevice)) != 0) {
32933 if (pContext->logCallback) {
32934 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.");
32938 if (pConfig->playback.pDeviceID != NULL) {
32939 MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id));
32942 if (pConfig->capture.pDeviceID != NULL) {
32943 MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id));
32946 pDevice->noPreZeroedOutputBuffer = pConfig->noPreZeroedOutputBuffer;
32947 pDevice->noClip = pConfig->noClip;
32948 pDevice->masterVolumeFactor = 1;
32950 pDevice->type = pConfig->deviceType;
32951 pDevice->sampleRate = pConfig->sampleRate;
32952 pDevice->resampling.algorithm = pConfig->resampling.algorithm;
32953 pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder;
32954 pDevice->resampling.speex.quality = pConfig->resampling.speex.quality;
32956 pDevice->capture.shareMode = pConfig->capture.shareMode;
32957 pDevice->capture.format = pConfig->capture.format;
32958 pDevice->capture.channels = pConfig->capture.channels;
32959 ma_channel_map_copy(pDevice->capture.channelMap, pConfig->capture.channelMap, pConfig->capture.channels);
32960 pDevice->capture.channelMixMode = pConfig->capture.channelMixMode;
32962 pDevice->playback.shareMode = pConfig->playback.shareMode;
32963 pDevice->playback.format = pConfig->playback.format;
32964 pDevice->playback.channels = pConfig->playback.channels;
32965 ma_channel_map_copy(pDevice->playback.channelMap, pConfig->playback.channelMap, pConfig->playback.channels);
32966 pDevice->playback.channelMixMode = pConfig->playback.channelMixMode;
32969 result = ma_mutex_init(&pDevice->startStopLock);
32970 if (result != MA_SUCCESS) {
32971 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create mutex.", result);
32975 When the device is started, the worker thread is the one that does the actual startup of the backend device. We
32976 use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device.
32978 Each of these semaphores is released internally by the worker thread when the work is completed. The start
32979 semaphore is also used to wake up the worker thread.
32981 result = ma_event_init(&pDevice->wakeupEvent);
32982 if (result != MA_SUCCESS) {
32983 ma_mutex_uninit(&pDevice->startStopLock);
32984 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread wakeup event.", result);
32987 result = ma_event_init(&pDevice->startEvent);
32988 if (result != MA_SUCCESS) {
32989 ma_event_uninit(&pDevice->wakeupEvent);
32990 ma_mutex_uninit(&pDevice->startStopLock);
32991 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread start event.", result);
32994 result = ma_event_init(&pDevice->stopEvent);
32995 if (result != MA_SUCCESS) {
32996 ma_event_uninit(&pDevice->startEvent);
32997 ma_event_uninit(&pDevice->wakeupEvent);
32998 ma_mutex_uninit(&pDevice->startStopLock);
32999 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread stop event.", result);
33003 MA_ZERO_OBJECT(&descriptorPlayback);
33004 descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID;
33005 descriptorPlayback.shareMode = pConfig->playback.shareMode;
33006 descriptorPlayback.format = pConfig->playback.format;
33007 descriptorPlayback.channels = pConfig->playback.channels;
33008 descriptorPlayback.sampleRate = pConfig->sampleRate;
33009 ma_channel_map_copy(descriptorPlayback.channelMap, pConfig->playback.channelMap, pConfig->playback.channels);
33010 descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames;
33011 descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
33012 descriptorPlayback.periodCount = pConfig->periods;
33014 if (descriptorPlayback.periodCount == 0) {
33015 descriptorPlayback.periodCount = MA_DEFAULT_PERIODS;
33019 MA_ZERO_OBJECT(&descriptorCapture);
33020 descriptorCapture.pDeviceID = pConfig->capture.pDeviceID;
33021 descriptorCapture.shareMode = pConfig->capture.shareMode;
33022 descriptorCapture.format = pConfig->capture.format;
33023 descriptorCapture.channels = pConfig->capture.channels;
33024 descriptorCapture.sampleRate = pConfig->sampleRate;
33025 ma_channel_map_copy(descriptorCapture.channelMap, pConfig->capture.channelMap, pConfig->capture.channels);
33026 descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames;
33027 descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
33028 descriptorCapture.periodCount = pConfig->periods;
33030 if (descriptorCapture.periodCount == 0) {
33031 descriptorCapture.periodCount = MA_DEFAULT_PERIODS;
33035 result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture);
33036 if (result != MA_SUCCESS) {
33037 ma_event_uninit(&pDevice->startEvent);
33038 ma_event_uninit(&pDevice->wakeupEvent);
33039 ma_mutex_uninit(&pDevice->startStopLock);
33045 On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between
33046 the requested format and the internal format.
33048 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
33049 if (!ma_device_descriptor_is_valid(&descriptorCapture)) {
33050 ma_device_uninit(pDevice);
33051 return MA_INVALID_ARGS;
33054 pDevice->capture.internalFormat = descriptorCapture.format;
33055 pDevice->capture.internalChannels = descriptorCapture.channels;
33056 pDevice->capture.internalSampleRate = descriptorCapture.sampleRate;
33057 ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels);
33058 pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames;
33059 pDevice->capture.internalPeriods = descriptorCapture.periodCount;
33061 if (pDevice->capture.internalPeriodSizeInFrames == 0) {
33062 pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorCapture.periodSizeInMilliseconds, descriptorCapture.sampleRate);
33066 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
33067 if (!ma_device_descriptor_is_valid(&descriptorPlayback)) {
33068 ma_device_uninit(pDevice);
33069 return MA_INVALID_ARGS;
33072 pDevice->playback.internalFormat = descriptorPlayback.format;
33073 pDevice->playback.internalChannels = descriptorPlayback.channels;
33074 pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate;
33075 ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels);
33076 pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames;
33077 pDevice->playback.internalPeriods = descriptorPlayback.periodCount;
33079 if (pDevice->playback.internalPeriodSizeInFrames == 0) {
33080 pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorPlayback.periodSizeInMilliseconds, descriptorPlayback.sampleRate);
33086 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.
33087 For loopback devices, we need to retrieve the name of the playback device.
33090 ma_device_info deviceInfo;
33092 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
33093 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);
33094 if (result == MA_SUCCESS) {
33095 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1);
33097 /* We failed to retrieve the device info. Fall back to a default name. */
33098 if (descriptorCapture.pDeviceID == NULL) {
33099 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
33101 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1);
33106 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
33107 result = ma_context_get_device_info(pContext, ma_device_type_playback, descriptorPlayback.pDeviceID, descriptorPlayback.shareMode, &deviceInfo);
33108 if (result == MA_SUCCESS) {
33109 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1);
33111 /* We failed to retrieve the device info. Fall back to a default name. */
33112 if (descriptorPlayback.pDeviceID == NULL) {
33113 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
33115 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1);
33122 ma_device__post_init_setup(pDevice, pConfig->deviceType);
33125 /* Some backends don't require the worker thread. */
33126 if (!ma_context_is_backend_asynchronous(pContext)) {
33127 /* The worker thread. */
33128 result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice);
33129 if (result != MA_SUCCESS) {
33130 ma_device_uninit(pDevice);
33131 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread.", result);
33134 /* Wait for the worker thread to put the device into it's stopped state for real. */
33135 ma_event_wait(&pDevice->stopEvent);
33136 MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_STOPPED);
33139 If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done
33140 after ma_device__post_init_setup().
33142 if (ma_context_is_backend_asynchronous(pContext)) {
33143 if (pConfig->deviceType == ma_device_type_duplex) {
33144 result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);
33145 if (result != MA_SUCCESS) {
33146 ma_device_uninit(pDevice);
33152 ma_device__set_state(pDevice, MA_STATE_STOPPED);
33156 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, "[%s]", ma_get_backend_name(pDevice->pContext->backend));
33157 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
33158 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " %s (%s)", pDevice->capture.name, "Capture");
33159 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Format: %s -> %s", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format));
33160 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Channels: %d -> %d", pDevice->capture.internalChannels, pDevice->capture.channels);
33161 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d", pDevice->capture.internalSampleRate, pDevice->sampleRate);
33162 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods));
33163 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Conversion:");
33164 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO");
33165 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Post Format Conversion: %s", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO");
33166 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Channel Routing: %s", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO");
33167 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Resampling: %s", pDevice->capture.converter.hasResampler ? "YES" : "NO");
33168 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Passthrough: %s", pDevice->capture.converter.isPassthrough ? "YES" : "NO");
33170 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
33171 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " %s (%s)", pDevice->playback.name, "Playback");
33172 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Format: %s -> %s", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat));
33173 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Channels: %d -> %d", pDevice->playback.channels, pDevice->playback.internalChannels);
33174 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d", pDevice->sampleRate, pDevice->playback.internalSampleRate);
33175 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods));
33176 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Conversion:");
33177 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO");
33178 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Post Format Conversion: %s", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO");
33179 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Channel Routing: %s", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO");
33180 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Resampling: %s", pDevice->playback.converter.hasResampler ? "YES" : "NO");
33181 ma_post_log_messagef(pContext, pDevice, MA_LOG_LEVEL_INFO, " Passthrough: %s", pDevice->playback.converter.isPassthrough ? "YES" : "NO");
33184 MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_STOPPED);
33188 MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice)
33191 ma_context* pContext;
33192 ma_backend defaultBackends[ma_backend_null+1];
33193 ma_uint32 iBackend;
33194 ma_backend* pBackendsToIterate;
33195 ma_uint32 backendsToIterateCount;
33196 ma_allocation_callbacks allocationCallbacks;
33198 if (pConfig == NULL) {
33199 return MA_INVALID_ARGS;
33202 if (pContextConfig != NULL) {
33203 result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks);
33204 if (result != MA_SUCCESS) {
33208 allocationCallbacks = ma_allocation_callbacks_init_default();
33212 pContext = (ma_context*)ma__malloc_from_callbacks(sizeof(*pContext), &allocationCallbacks);
33213 if (pContext == NULL) {
33214 return MA_OUT_OF_MEMORY;
33217 for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
33218 defaultBackends[iBackend] = (ma_backend)iBackend;
33221 pBackendsToIterate = (ma_backend*)backends;
33222 backendsToIterateCount = backendCount;
33223 if (pBackendsToIterate == NULL) {
33224 pBackendsToIterate = (ma_backend*)defaultBackends;
33225 backendsToIterateCount = ma_countof(defaultBackends);
33228 result = MA_NO_BACKEND;
33230 for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
33231 result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext);
33232 if (result == MA_SUCCESS) {
33233 result = ma_device_init(pContext, pConfig, pDevice);
33234 if (result == MA_SUCCESS) {
33235 break; /* Success. */
33237 ma_context_uninit(pContext); /* Failure. */
33242 if (result != MA_SUCCESS) {
33243 ma__free_from_callbacks(pContext, &allocationCallbacks);
33247 pDevice->isOwnerOfContext = MA_TRUE;
33251 MA_API void ma_device_uninit(ma_device* pDevice)
33253 if (!ma_device__is_initialized(pDevice)) {
33257 /* 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. */
33258 if (ma_device_is_started(pDevice)) {
33259 ma_device_stop(pDevice);
33262 /* Putting the device into an uninitialized state will make the worker thread return. */
33263 ma_device__set_state(pDevice, MA_STATE_UNINITIALIZED);
33265 /* Wake up the worker thread and wait for it to properly terminate. */
33266 if (!ma_context_is_backend_asynchronous(pDevice->pContext)) {
33267 ma_event_signal(&pDevice->wakeupEvent);
33268 ma_thread_wait(&pDevice->thread);
33271 if (pDevice->pContext->callbacks.onDeviceUninit != NULL) {
33272 pDevice->pContext->callbacks.onDeviceUninit(pDevice);
33276 ma_event_uninit(&pDevice->stopEvent);
33277 ma_event_uninit(&pDevice->startEvent);
33278 ma_event_uninit(&pDevice->wakeupEvent);
33279 ma_mutex_uninit(&pDevice->startStopLock);
33281 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
33282 if (pDevice->type == ma_device_type_duplex) {
33283 ma_duplex_rb_uninit(&pDevice->duplexRB);
33287 if (pDevice->isOwnerOfContext) {
33288 ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks;
33290 ma_context_uninit(pDevice->pContext);
33291 ma__free_from_callbacks(pDevice->pContext, &allocationCallbacks);
33294 MA_ZERO_OBJECT(pDevice);
33297 MA_API ma_result ma_device_start(ma_device* pDevice)
33301 if (pDevice == NULL) {
33302 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_start() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
33305 if (ma_device_get_state(pDevice) == MA_STATE_UNINITIALIZED) {
33306 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_start() called for an uninitialized device.", MA_DEVICE_NOT_INITIALIZED);
33309 if (ma_device_get_state(pDevice) == MA_STATE_STARTED) {
33310 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. */
33313 ma_mutex_lock(&pDevice->startStopLock);
33315 /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */
33316 MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_STOPPED);
33318 ma_device__set_state(pDevice, MA_STATE_STARTING);
33320 /* Asynchronous backends need to be handled differently. */
33321 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
33322 if (pDevice->pContext->callbacks.onDeviceStart != NULL) {
33323 result = pDevice->pContext->callbacks.onDeviceStart(pDevice);
33325 result = MA_INVALID_OPERATION;
33328 if (result == MA_SUCCESS) {
33329 ma_device__set_state(pDevice, MA_STATE_STARTED);
33333 Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the
33334 thread and then wait for the start event.
33336 ma_event_signal(&pDevice->wakeupEvent);
33339 Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device
33340 into the started state. Don't call ma_device__set_state() here.
33342 ma_event_wait(&pDevice->startEvent);
33343 result = pDevice->workResult;
33346 /* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */
33347 if (result != MA_SUCCESS) {
33348 ma_device__set_state(pDevice, MA_STATE_STOPPED);
33351 ma_mutex_unlock(&pDevice->startStopLock);
33356 MA_API ma_result ma_device_stop(ma_device* pDevice)
33360 if (pDevice == NULL) {
33361 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_stop() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
33364 if (ma_device_get_state(pDevice) == MA_STATE_UNINITIALIZED) {
33365 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_stop() called for an uninitialized device.", MA_DEVICE_NOT_INITIALIZED);
33368 if (ma_device_get_state(pDevice) == MA_STATE_STOPPED) {
33369 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. */
33372 ma_mutex_lock(&pDevice->startStopLock);
33374 /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */
33375 MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_STARTED);
33377 ma_device__set_state(pDevice, MA_STATE_STOPPING);
33379 /* Asynchronous backends need to be handled differently. */
33380 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
33381 /* Asynchronous backends must have a stop operation. */
33382 if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
33383 result = pDevice->pContext->callbacks.onDeviceStop(pDevice);
33385 result = MA_INVALID_OPERATION;
33388 ma_device__set_state(pDevice, MA_STATE_STOPPED);
33391 Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If
33392 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
33393 sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super
33394 important though, so I'm asserting it here as well for extra safety in case we accidentally change something later.
33396 MA_ASSERT(ma_device_get_state(pDevice) != MA_STATE_STARTED);
33398 if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) {
33399 pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice);
33403 We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be
33404 the one who puts the device into the stopped state. Don't call ma_device__set_state() here.
33406 ma_event_wait(&pDevice->stopEvent);
33407 result = MA_SUCCESS;
33410 ma_mutex_unlock(&pDevice->startStopLock);
33415 MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice)
33417 return ma_device_get_state(pDevice) == MA_STATE_STARTED;
33420 MA_API ma_uint32 ma_device_get_state(const ma_device* pDevice)
33422 if (pDevice == NULL) {
33423 return MA_STATE_UNINITIALIZED;
33426 return c89atomic_load_32((ma_uint32*)&pDevice->state); /* Naughty cast to get rid of a const warning. */
33429 MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume)
33431 if (pDevice == NULL) {
33432 return MA_INVALID_ARGS;
33435 if (volume < 0.0f || volume > 1.0f) {
33436 return MA_INVALID_ARGS;
33439 c89atomic_exchange_f32(&pDevice->masterVolumeFactor, volume);
33444 MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume)
33446 if (pVolume == NULL) {
33447 return MA_INVALID_ARGS;
33450 if (pDevice == NULL) {
33452 return MA_INVALID_ARGS;
33455 *pVolume = c89atomic_load_f32(&pDevice->masterVolumeFactor);
33460 MA_API ma_result ma_device_set_master_gain_db(ma_device* pDevice, float gainDB)
33463 return MA_INVALID_ARGS;
33466 return ma_device_set_master_volume(pDevice, ma_gain_db_to_factor(gainDB));
33469 MA_API ma_result ma_device_get_master_gain_db(ma_device* pDevice, float* pGainDB)
33474 if (pGainDB == NULL) {
33475 return MA_INVALID_ARGS;
33478 result = ma_device_get_master_volume(pDevice, &factor);
33479 if (result != MA_SUCCESS) {
33484 *pGainDB = ma_factor_to_gain_db(factor);
33490 MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
33492 if (pDevice == NULL) {
33493 return MA_INVALID_ARGS;
33496 if (pOutput == NULL && pInput == NULL) {
33497 return MA_INVALID_ARGS;
33500 if (pDevice->type == ma_device_type_duplex) {
33501 if (pInput != NULL) {
33502 ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb);
33505 if (pOutput != NULL) {
33506 ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb);
33509 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) {
33510 if (pInput == NULL) {
33511 return MA_INVALID_ARGS;
33514 ma_device__send_frames_to_client(pDevice, frameCount, pInput);
33517 if (pDevice->type == ma_device_type_playback) {
33518 if (pOutput == NULL) {
33519 return MA_INVALID_ARGS;
33522 ma_device__read_frames_from_client(pDevice, frameCount, pOutput);
33529 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
33531 if (pDescriptor == NULL) {
33536 We must have a non-0 native sample rate, but some backends don't allow retrieval of this at the
33537 time when the size of the buffer needs to be determined. In this case we need to just take a best
33538 guess and move on. We'll try using the sample rate in pDescriptor first. If that's not set we'll
33539 just fall back to MA_DEFAULT_SAMPLE_RATE.
33541 if (nativeSampleRate == 0) {
33542 nativeSampleRate = pDescriptor->sampleRate;
33544 if (nativeSampleRate == 0) {
33545 nativeSampleRate = MA_DEFAULT_SAMPLE_RATE;
33548 MA_ASSERT(nativeSampleRate != 0);
33550 if (pDescriptor->periodSizeInFrames == 0) {
33551 if (pDescriptor->periodSizeInMilliseconds == 0) {
33552 if (performanceProfile == ma_performance_profile_low_latency) {
33553 return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate);
33555 return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate);
33558 return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);
33561 return pDescriptor->periodSizeInFrames;
33564 #endif /* MA_NO_DEVICE_IO */
33567 MA_API ma_uint32 ma_scale_buffer_size(ma_uint32 baseBufferSize, float scale)
33569 return ma_max(1, (ma_uint32)(baseBufferSize*scale));
33572 MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate)
33574 /* Prevent a division by zero. */
33575 if (sampleRate == 0) {
33579 return bufferSizeInFrames*1000 / sampleRate;
33582 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate)
33584 /* Prevent a division by zero. */
33585 if (sampleRate == 0) {
33589 return bufferSizeInMilliseconds*sampleRate / 1000;
33592 MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
33595 return; /* No-op. */
33598 ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels));
33601 MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
33603 if (format == ma_format_u8) {
33604 ma_uint64 sampleCount = frameCount * channels;
33606 for (iSample = 0; iSample < sampleCount; iSample += 1) {
33607 ((ma_uint8*)p)[iSample] = 128;
33610 ma_zero_memory_64(p, frameCount * ma_get_bytes_per_frame(format, channels));
33614 MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)
33616 return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels));
33619 MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)
33621 return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels));
33625 MA_API void ma_clip_samples_f32(float* p, ma_uint64 sampleCount)
33629 /* TODO: Research a branchless SSE implementation. */
33630 for (iSample = 0; iSample < sampleCount; iSample += 1) {
33631 p[iSample] = ma_clip_f32(p[iSample]);
33636 MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor)
33640 if (pSamplesOut == NULL || pSamplesIn == NULL) {
33644 for (iSample = 0; iSample < sampleCount; iSample += 1) {
33645 pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor);
33649 MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor)
33653 if (pSamplesOut == NULL || pSamplesIn == NULL) {
33657 for (iSample = 0; iSample < sampleCount; iSample += 1) {
33658 pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor);
33662 MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor)
33665 ma_uint8* pSamplesOut8;
33666 ma_uint8* pSamplesIn8;
33668 if (pSamplesOut == NULL || pSamplesIn == NULL) {
33672 pSamplesOut8 = (ma_uint8*)pSamplesOut;
33673 pSamplesIn8 = (ma_uint8*)pSamplesIn;
33675 for (iSample = 0; iSample < sampleCount; iSample += 1) {
33676 ma_int32 sampleS32;
33678 sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24);
33679 sampleS32 = (ma_int32)(sampleS32 * factor);
33681 pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8);
33682 pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16);
33683 pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24);
33687 MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor)
33691 if (pSamplesOut == NULL || pSamplesIn == NULL) {
33695 for (iSample = 0; iSample < sampleCount; iSample += 1) {
33696 pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor);
33700 MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor)
33704 if (pSamplesOut == NULL || pSamplesIn == NULL) {
33708 for (iSample = 0; iSample < sampleCount; iSample += 1) {
33709 pSamplesOut[iSample] = pSamplesIn[iSample] * factor;
33713 MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor)
33715 ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor);
33718 MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor)
33720 ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor);
33723 MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor)
33725 ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor);
33728 MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor)
33730 ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor);
33733 MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor)
33735 ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor);
33738 MA_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)
33740 ma_copy_and_apply_volume_factor_u8(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
33743 MA_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)
33745 ma_copy_and_apply_volume_factor_s16(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
33748 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
33750 ma_copy_and_apply_volume_factor_s24(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
33753 MA_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)
33755 ma_copy_and_apply_volume_factor_s32(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
33758 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
33760 ma_copy_and_apply_volume_factor_f32(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
33763 MA_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)
33767 case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pPCMFramesOut, (const ma_uint8*)pPCMFramesIn, frameCount, channels, factor); return;
33768 case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pPCMFramesOut, (const ma_int16*)pPCMFramesIn, frameCount, channels, factor); return;
33769 case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pPCMFramesOut, pPCMFramesIn, frameCount, channels, factor); return;
33770 case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pPCMFramesOut, (const ma_int32*)pPCMFramesIn, frameCount, channels, factor); return;
33771 case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pPCMFramesOut, (const float*)pPCMFramesIn, frameCount, channels, factor); return;
33772 default: return; /* Do nothing. */
33776 MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
33778 ma_copy_and_apply_volume_factor_pcm_frames_u8(pPCMFrames, pPCMFrames, frameCount, channels, factor);
33781 MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
33783 ma_copy_and_apply_volume_factor_pcm_frames_s16(pPCMFrames, pPCMFrames, frameCount, channels, factor);
33786 MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
33788 ma_copy_and_apply_volume_factor_pcm_frames_s24(pPCMFrames, pPCMFrames, frameCount, channels, factor);
33791 MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
33793 ma_copy_and_apply_volume_factor_pcm_frames_s32(pPCMFrames, pPCMFrames, frameCount, channels, factor);
33796 MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
33798 ma_copy_and_apply_volume_factor_pcm_frames_f32(pPCMFrames, pPCMFrames, frameCount, channels, factor);
33801 MA_API void ma_apply_volume_factor_pcm_frames(void* pPCMFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor)
33803 ma_copy_and_apply_volume_factor_pcm_frames(pPCMFrames, pPCMFrames, frameCount, format, channels, factor);
33807 MA_API float ma_factor_to_gain_db(float factor)
33809 return (float)(20*ma_log10f(factor));
33812 MA_API float ma_gain_db_to_factor(float gain)
33814 return (float)ma_powf(10, gain/20.0f);
33818 /**************************************************************************************************************************************************************
33822 **************************************************************************************************************************************************************/
33824 static MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x)
33826 return (ma_int16)(x * 32767.0f);
33829 static MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x)
33831 return (ma_int16)((ma_int16)x - 128);
33834 static MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8* x)
33836 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. */
33839 static MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s24)
33841 s24[0] = (ma_uint8)((x & 0x000000FF) >> 0);
33842 s24[1] = (ma_uint8)((x & 0x0000FF00) >> 8);
33843 s24[2] = (ma_uint8)((x & 0x00FF0000) >> 16);
33847 static MA_INLINE ma_uint8 ma_clip_u8(ma_int16 x)
33849 return (ma_uint8)(ma_clamp(x, -128, 127) + 128);
33852 static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x)
33854 return (ma_int16)ma_clamp(x, -32768, 32767);
33857 static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x)
33859 return (ma_int64)ma_clamp(x, -8388608, 8388607);
33862 static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x)
33864 /* This dance is to silence warnings with -std=c89. A good compiler should be able to optimize this away. */
33867 clipMin = -((ma_int64)2147483647 + 1);
33868 clipMax = (ma_int64)2147483647;
33870 return (ma_int32)ma_clamp(x, clipMin, clipMax);
33875 MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33878 ma_copy_memory_64(dst, src, count * sizeof(ma_uint8));
33882 static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33884 ma_int16* dst_s16 = (ma_int16*)dst;
33885 const ma_uint8* src_u8 = (const ma_uint8*)src;
33888 for (i = 0; i < count; i += 1) {
33889 ma_int16 x = src_u8[i];
33890 x = (ma_int16)(x - 128);
33891 x = (ma_int16)(x << 8);
33898 static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33900 ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
33903 #if defined(MA_SUPPORT_SSE2)
33904 static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33906 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
33909 #if defined(MA_SUPPORT_AVX2)
33910 static MA_INLINE void ma_pcm_u8_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33912 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
33915 #if defined(MA_SUPPORT_NEON)
33916 static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33918 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
33922 MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33924 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
33925 ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
33927 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
33928 if (ma_has_avx2()) {
33929 ma_pcm_u8_to_s16__avx2(dst, src, count, ditherMode);
33931 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
33932 if (ma_has_sse2()) {
33933 ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode);
33935 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
33936 if (ma_has_neon()) {
33937 ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode);
33941 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
33947 static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33949 ma_uint8* dst_s24 = (ma_uint8*)dst;
33950 const ma_uint8* src_u8 = (const ma_uint8*)src;
33953 for (i = 0; i < count; i += 1) {
33954 ma_int16 x = src_u8[i];
33955 x = (ma_int16)(x - 128);
33957 dst_s24[i*3+0] = 0;
33958 dst_s24[i*3+1] = 0;
33959 dst_s24[i*3+2] = (ma_uint8)((ma_int8)x);
33965 static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33967 ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
33970 #if defined(MA_SUPPORT_SSE2)
33971 static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33973 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
33976 #if defined(MA_SUPPORT_AVX2)
33977 static MA_INLINE void ma_pcm_u8_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33979 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
33982 #if defined(MA_SUPPORT_NEON)
33983 static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33985 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
33989 MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33991 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
33992 ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
33994 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
33995 if (ma_has_avx2()) {
33996 ma_pcm_u8_to_s24__avx2(dst, src, count, ditherMode);
33998 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
33999 if (ma_has_sse2()) {
34000 ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode);
34002 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34003 if (ma_has_neon()) {
34004 ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode);
34008 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
34014 static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34016 ma_int32* dst_s32 = (ma_int32*)dst;
34017 const ma_uint8* src_u8 = (const ma_uint8*)src;
34020 for (i = 0; i < count; i += 1) {
34021 ma_int32 x = src_u8[i];
34030 static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34032 ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
34035 #if defined(MA_SUPPORT_SSE2)
34036 static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34038 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
34041 #if defined(MA_SUPPORT_AVX2)
34042 static MA_INLINE void ma_pcm_u8_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34044 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
34047 #if defined(MA_SUPPORT_NEON)
34048 static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34050 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
34054 MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34056 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
34057 ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
34059 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
34060 if (ma_has_avx2()) {
34061 ma_pcm_u8_to_s32__avx2(dst, src, count, ditherMode);
34063 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
34064 if (ma_has_sse2()) {
34065 ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode);
34067 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34068 if (ma_has_neon()) {
34069 ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode);
34073 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
34079 static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34081 float* dst_f32 = (float*)dst;
34082 const ma_uint8* src_u8 = (const ma_uint8*)src;
34085 for (i = 0; i < count; i += 1) {
34086 float x = (float)src_u8[i];
34087 x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */
34088 x = x - 1; /* 0..2 to -1..1 */
34096 static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34098 ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
34101 #if defined(MA_SUPPORT_SSE2)
34102 static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34104 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
34107 #if defined(MA_SUPPORT_AVX2)
34108 static MA_INLINE void ma_pcm_u8_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34110 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
34113 #if defined(MA_SUPPORT_NEON)
34114 static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34116 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
34120 MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34122 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
34123 ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
34125 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
34126 if (ma_has_avx2()) {
34127 ma_pcm_u8_to_f32__avx2(dst, src, count, ditherMode);
34129 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
34130 if (ma_has_sse2()) {
34131 ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode);
34133 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34134 if (ma_has_neon()) {
34135 ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode);
34139 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
34145 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
34146 static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
34148 ma_uint8* dst_u8 = (ma_uint8*)dst;
34149 const ma_uint8** src_u8 = (const ma_uint8**)src;
34152 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34153 ma_uint32 iChannel;
34154 for (iChannel = 0; iChannel < channels; iChannel += 1) {
34155 dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
34160 static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
34162 ma_uint8* dst_u8 = (ma_uint8*)dst;
34163 const ma_uint8** src_u8 = (const ma_uint8**)src;
34165 if (channels == 1) {
34166 ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8));
34167 } else if (channels == 2) {
34169 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34170 dst_u8[iFrame*2 + 0] = src_u8[0][iFrame];
34171 dst_u8[iFrame*2 + 1] = src_u8[1][iFrame];
34175 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34176 ma_uint32 iChannel;
34177 for (iChannel = 0; iChannel < channels; iChannel += 1) {
34178 dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
34185 MA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
34187 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
34188 ma_pcm_interleave_u8__reference(dst, src, frameCount, channels);
34190 ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels);
34195 static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
34197 ma_uint8** dst_u8 = (ma_uint8**)dst;
34198 const ma_uint8* src_u8 = (const ma_uint8*)src;
34201 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34202 ma_uint32 iChannel;
34203 for (iChannel = 0; iChannel < channels; iChannel += 1) {
34204 dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel];
34209 static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
34211 ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
34214 MA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
34216 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
34217 ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
34219 ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels);
34225 static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34227 ma_uint8* dst_u8 = (ma_uint8*)dst;
34228 const ma_int16* src_s16 = (const ma_int16*)src;
34230 if (ditherMode == ma_dither_mode_none) {
34232 for (i = 0; i < count; i += 1) {
34233 ma_int16 x = src_s16[i];
34234 x = (ma_int16)(x >> 8);
34235 x = (ma_int16)(x + 128);
34236 dst_u8[i] = (ma_uint8)x;
34240 for (i = 0; i < count; i += 1) {
34241 ma_int16 x = src_s16[i];
34243 /* Dither. Don't overflow. */
34244 ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F);
34245 if ((x + dither) <= 0x7FFF) {
34246 x = (ma_int16)(x + dither);
34251 x = (ma_int16)(x >> 8);
34252 x = (ma_int16)(x + 128);
34253 dst_u8[i] = (ma_uint8)x;
34258 static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34260 ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
34263 #if defined(MA_SUPPORT_SSE2)
34264 static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34266 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
34269 #if defined(MA_SUPPORT_AVX2)
34270 static MA_INLINE void ma_pcm_s16_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34272 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
34275 #if defined(MA_SUPPORT_NEON)
34276 static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34278 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
34282 MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34284 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
34285 ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
34287 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
34288 if (ma_has_avx2()) {
34289 ma_pcm_s16_to_u8__avx2(dst, src, count, ditherMode);
34291 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
34292 if (ma_has_sse2()) {
34293 ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode);
34295 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34296 if (ma_has_neon()) {
34297 ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode);
34301 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
34307 MA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34310 ma_copy_memory_64(dst, src, count * sizeof(ma_int16));
34314 static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34316 ma_uint8* dst_s24 = (ma_uint8*)dst;
34317 const ma_int16* src_s16 = (const ma_int16*)src;
34320 for (i = 0; i < count; i += 1) {
34321 dst_s24[i*3+0] = 0;
34322 dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF);
34323 dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8);
34329 static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34331 ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
34334 #if defined(MA_SUPPORT_SSE2)
34335 static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34337 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
34340 #if defined(MA_SUPPORT_AVX2)
34341 static MA_INLINE void ma_pcm_s16_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34343 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
34346 #if defined(MA_SUPPORT_NEON)
34347 static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34349 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
34353 MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34355 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
34356 ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
34358 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
34359 if (ma_has_avx2()) {
34360 ma_pcm_s16_to_s24__avx2(dst, src, count, ditherMode);
34362 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
34363 if (ma_has_sse2()) {
34364 ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode);
34366 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34367 if (ma_has_neon()) {
34368 ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode);
34372 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
34378 static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34380 ma_int32* dst_s32 = (ma_int32*)dst;
34381 const ma_int16* src_s16 = (const ma_int16*)src;
34384 for (i = 0; i < count; i += 1) {
34385 dst_s32[i] = src_s16[i] << 16;
34391 static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34393 ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
34396 #if defined(MA_SUPPORT_SSE2)
34397 static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34399 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
34402 #if defined(MA_SUPPORT_AVX2)
34403 static MA_INLINE void ma_pcm_s16_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34405 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
34408 #if defined(MA_SUPPORT_NEON)
34409 static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34411 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
34415 MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34417 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
34418 ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
34420 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
34421 if (ma_has_avx2()) {
34422 ma_pcm_s16_to_s32__avx2(dst, src, count, ditherMode);
34424 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
34425 if (ma_has_sse2()) {
34426 ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode);
34428 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34429 if (ma_has_neon()) {
34430 ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode);
34434 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
34440 static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34442 float* dst_f32 = (float*)dst;
34443 const ma_int16* src_s16 = (const ma_int16*)src;
34446 for (i = 0; i < count; i += 1) {
34447 float x = (float)src_s16[i];
34450 /* The accurate way. */
34451 x = x + 32768.0f; /* -32768..32767 to 0..65535 */
34452 x = x * 0.00003051804379339284f; /* 0..65535 to 0..2 */
34453 x = x - 1; /* 0..2 to -1..1 */
34455 /* The fast way. */
34456 x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */
34465 static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34467 ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
34470 #if defined(MA_SUPPORT_SSE2)
34471 static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34473 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
34476 #if defined(MA_SUPPORT_AVX2)
34477 static MA_INLINE void ma_pcm_s16_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34479 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
34482 #if defined(MA_SUPPORT_NEON)
34483 static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34485 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
34489 MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34491 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
34492 ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
34494 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
34495 if (ma_has_avx2()) {
34496 ma_pcm_s16_to_f32__avx2(dst, src, count, ditherMode);
34498 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
34499 if (ma_has_sse2()) {
34500 ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode);
34502 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34503 if (ma_has_neon()) {
34504 ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode);
34508 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
34514 static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
34516 ma_int16* dst_s16 = (ma_int16*)dst;
34517 const ma_int16** src_s16 = (const ma_int16**)src;
34520 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34521 ma_uint32 iChannel;
34522 for (iChannel = 0; iChannel < channels; iChannel += 1) {
34523 dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame];
34528 static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
34530 ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
34533 MA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
34535 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
34536 ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
34538 ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels);
34543 static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
34545 ma_int16** dst_s16 = (ma_int16**)dst;
34546 const ma_int16* src_s16 = (const ma_int16*)src;
34549 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34550 ma_uint32 iChannel;
34551 for (iChannel = 0; iChannel < channels; iChannel += 1) {
34552 dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel];
34557 static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
34559 ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
34562 MA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
34564 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
34565 ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
34567 ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels);
34573 static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34575 ma_uint8* dst_u8 = (ma_uint8*)dst;
34576 const ma_uint8* src_s24 = (const ma_uint8*)src;
34578 if (ditherMode == ma_dither_mode_none) {
34580 for (i = 0; i < count; i += 1) {
34581 dst_u8[i] = (ma_uint8)((ma_int8)src_s24[i*3 + 2] + 128);
34585 for (i = 0; i < count; i += 1) {
34586 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);
34588 /* Dither. Don't overflow. */
34589 ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
34590 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
34598 dst_u8[i] = (ma_uint8)x;
34603 static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34605 ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
34608 #if defined(MA_SUPPORT_SSE2)
34609 static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34611 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
34614 #if defined(MA_SUPPORT_AVX2)
34615 static MA_INLINE void ma_pcm_s24_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34617 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
34620 #if defined(MA_SUPPORT_NEON)
34621 static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34623 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
34627 MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34629 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
34630 ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
34632 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
34633 if (ma_has_avx2()) {
34634 ma_pcm_s24_to_u8__avx2(dst, src, count, ditherMode);
34636 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
34637 if (ma_has_sse2()) {
34638 ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode);
34640 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34641 if (ma_has_neon()) {
34642 ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode);
34646 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
34652 static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34654 ma_int16* dst_s16 = (ma_int16*)dst;
34655 const ma_uint8* src_s24 = (const ma_uint8*)src;
34657 if (ditherMode == ma_dither_mode_none) {
34659 for (i = 0; i < count; i += 1) {
34660 ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]);
34661 ma_uint16 dst_hi = (ma_uint16)((ma_uint16)src_s24[i*3 + 2] << 8);
34662 dst_s16[i] = (ma_int16)(dst_lo | dst_hi);
34666 for (i = 0; i < count; i += 1) {
34667 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);
34669 /* Dither. Don't overflow. */
34670 ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
34671 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
34678 dst_s16[i] = (ma_int16)x;
34683 static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34685 ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
34688 #if defined(MA_SUPPORT_SSE2)
34689 static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34691 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
34694 #if defined(MA_SUPPORT_AVX2)
34695 static MA_INLINE void ma_pcm_s24_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34697 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
34700 #if defined(MA_SUPPORT_NEON)
34701 static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34703 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
34707 MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34709 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
34710 ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
34712 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
34713 if (ma_has_avx2()) {
34714 ma_pcm_s24_to_s16__avx2(dst, src, count, ditherMode);
34716 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
34717 if (ma_has_sse2()) {
34718 ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode);
34720 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34721 if (ma_has_neon()) {
34722 ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode);
34726 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
34732 MA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34736 ma_copy_memory_64(dst, src, count * 3);
34740 static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34742 ma_int32* dst_s32 = (ma_int32*)dst;
34743 const ma_uint8* src_s24 = (const ma_uint8*)src;
34746 for (i = 0; i < count; i += 1) {
34747 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);
34753 static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34755 ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
34758 #if defined(MA_SUPPORT_SSE2)
34759 static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34761 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
34764 #if defined(MA_SUPPORT_AVX2)
34765 static MA_INLINE void ma_pcm_s24_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34767 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
34770 #if defined(MA_SUPPORT_NEON)
34771 static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34773 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
34777 MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34779 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
34780 ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
34782 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
34783 if (ma_has_avx2()) {
34784 ma_pcm_s24_to_s32__avx2(dst, src, count, ditherMode);
34786 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
34787 if (ma_has_sse2()) {
34788 ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode);
34790 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34791 if (ma_has_neon()) {
34792 ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode);
34796 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
34802 static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34804 float* dst_f32 = (float*)dst;
34805 const ma_uint8* src_s24 = (const ma_uint8*)src;
34808 for (i = 0; i < count; i += 1) {
34809 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);
34812 /* The accurate way. */
34813 x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */
34814 x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */
34815 x = x - 1; /* 0..2 to -1..1 */
34817 /* The fast way. */
34818 x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */
34827 static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34829 ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
34832 #if defined(MA_SUPPORT_SSE2)
34833 static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34835 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
34838 #if defined(MA_SUPPORT_AVX2)
34839 static MA_INLINE void ma_pcm_s24_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34841 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
34844 #if defined(MA_SUPPORT_NEON)
34845 static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34847 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
34851 MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34853 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
34854 ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
34856 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
34857 if (ma_has_avx2()) {
34858 ma_pcm_s24_to_f32__avx2(dst, src, count, ditherMode);
34860 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
34861 if (ma_has_sse2()) {
34862 ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode);
34864 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34865 if (ma_has_neon()) {
34866 ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode);
34870 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
34876 static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
34878 ma_uint8* dst8 = (ma_uint8*)dst;
34879 const ma_uint8** src8 = (const ma_uint8**)src;
34882 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34883 ma_uint32 iChannel;
34884 for (iChannel = 0; iChannel < channels; iChannel += 1) {
34885 dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0];
34886 dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1];
34887 dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2];
34892 static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
34894 ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
34897 MA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
34899 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
34900 ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
34902 ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels);
34907 static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
34909 ma_uint8** dst8 = (ma_uint8**)dst;
34910 const ma_uint8* src8 = (const ma_uint8*)src;
34913 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34914 ma_uint32 iChannel;
34915 for (iChannel = 0; iChannel < channels; iChannel += 1) {
34916 dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0];
34917 dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1];
34918 dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2];
34923 static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
34925 ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
34928 MA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
34930 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
34931 ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
34933 ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels);
34940 static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34942 ma_uint8* dst_u8 = (ma_uint8*)dst;
34943 const ma_int32* src_s32 = (const ma_int32*)src;
34945 if (ditherMode == ma_dither_mode_none) {
34947 for (i = 0; i < count; i += 1) {
34948 ma_int32 x = src_s32[i];
34951 dst_u8[i] = (ma_uint8)x;
34955 for (i = 0; i < count; i += 1) {
34956 ma_int32 x = src_s32[i];
34958 /* Dither. Don't overflow. */
34959 ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
34960 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
34968 dst_u8[i] = (ma_uint8)x;
34973 static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34975 ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
34978 #if defined(MA_SUPPORT_SSE2)
34979 static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34981 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
34984 #if defined(MA_SUPPORT_AVX2)
34985 static MA_INLINE void ma_pcm_s32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34987 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
34990 #if defined(MA_SUPPORT_NEON)
34991 static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34993 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
34997 MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34999 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35000 ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
35002 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35003 if (ma_has_avx2()) {
35004 ma_pcm_s32_to_u8__avx2(dst, src, count, ditherMode);
35006 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35007 if (ma_has_sse2()) {
35008 ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode);
35010 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35011 if (ma_has_neon()) {
35012 ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode);
35016 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
35022 static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35024 ma_int16* dst_s16 = (ma_int16*)dst;
35025 const ma_int32* src_s32 = (const ma_int32*)src;
35027 if (ditherMode == ma_dither_mode_none) {
35029 for (i = 0; i < count; i += 1) {
35030 ma_int32 x = src_s32[i];
35032 dst_s16[i] = (ma_int16)x;
35036 for (i = 0; i < count; i += 1) {
35037 ma_int32 x = src_s32[i];
35039 /* Dither. Don't overflow. */
35040 ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
35041 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
35048 dst_s16[i] = (ma_int16)x;
35053 static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35055 ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
35058 #if defined(MA_SUPPORT_SSE2)
35059 static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35061 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
35064 #if defined(MA_SUPPORT_AVX2)
35065 static MA_INLINE void ma_pcm_s32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35067 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
35070 #if defined(MA_SUPPORT_NEON)
35071 static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35073 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
35077 MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35079 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35080 ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
35082 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35083 if (ma_has_avx2()) {
35084 ma_pcm_s32_to_s16__avx2(dst, src, count, ditherMode);
35086 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35087 if (ma_has_sse2()) {
35088 ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode);
35090 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35091 if (ma_has_neon()) {
35092 ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode);
35096 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
35102 static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35104 ma_uint8* dst_s24 = (ma_uint8*)dst;
35105 const ma_int32* src_s32 = (const ma_int32*)src;
35108 for (i = 0; i < count; i += 1) {
35109 ma_uint32 x = (ma_uint32)src_s32[i];
35110 dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8);
35111 dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16);
35112 dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24);
35115 (void)ditherMode; /* No dithering for s32 -> s24. */
35118 static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35120 ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
35123 #if defined(MA_SUPPORT_SSE2)
35124 static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35126 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
35129 #if defined(MA_SUPPORT_AVX2)
35130 static MA_INLINE void ma_pcm_s32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35132 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
35135 #if defined(MA_SUPPORT_NEON)
35136 static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35138 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
35142 MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35144 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35145 ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
35147 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35148 if (ma_has_avx2()) {
35149 ma_pcm_s32_to_s24__avx2(dst, src, count, ditherMode);
35151 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35152 if (ma_has_sse2()) {
35153 ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode);
35155 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35156 if (ma_has_neon()) {
35157 ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode);
35161 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
35167 MA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35171 ma_copy_memory_64(dst, src, count * sizeof(ma_int32));
35175 static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35177 float* dst_f32 = (float*)dst;
35178 const ma_int32* src_s32 = (const ma_int32*)src;
35181 for (i = 0; i < count; i += 1) {
35182 double x = src_s32[i];
35185 x = x + 2147483648.0;
35186 x = x * 0.0000000004656612873077392578125;
35189 x = x / 2147483648.0;
35192 dst_f32[i] = (float)x;
35195 (void)ditherMode; /* No dithering for s32 -> f32. */
35198 static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35200 ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
35203 #if defined(MA_SUPPORT_SSE2)
35204 static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35206 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
35209 #if defined(MA_SUPPORT_AVX2)
35210 static MA_INLINE void ma_pcm_s32_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35212 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
35215 #if defined(MA_SUPPORT_NEON)
35216 static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35218 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
35222 MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35224 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35225 ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
35227 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35228 if (ma_has_avx2()) {
35229 ma_pcm_s32_to_f32__avx2(dst, src, count, ditherMode);
35231 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35232 if (ma_has_sse2()) {
35233 ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode);
35235 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35236 if (ma_has_neon()) {
35237 ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode);
35241 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
35247 static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
35249 ma_int32* dst_s32 = (ma_int32*)dst;
35250 const ma_int32** src_s32 = (const ma_int32**)src;
35253 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
35254 ma_uint32 iChannel;
35255 for (iChannel = 0; iChannel < channels; iChannel += 1) {
35256 dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame];
35261 static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
35263 ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
35266 MA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
35268 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35269 ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
35271 ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels);
35276 static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
35278 ma_int32** dst_s32 = (ma_int32**)dst;
35279 const ma_int32* src_s32 = (const ma_int32*)src;
35282 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
35283 ma_uint32 iChannel;
35284 for (iChannel = 0; iChannel < channels; iChannel += 1) {
35285 dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel];
35290 static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
35292 ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
35295 MA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
35297 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35298 ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
35300 ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels);
35306 static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35310 ma_uint8* dst_u8 = (ma_uint8*)dst;
35311 const float* src_f32 = (const float*)src;
35313 float ditherMin = 0;
35314 float ditherMax = 0;
35315 if (ditherMode != ma_dither_mode_none) {
35316 ditherMin = 1.0f / -128;
35317 ditherMax = 1.0f / 127;
35320 for (i = 0; i < count; i += 1) {
35321 float x = src_f32[i];
35322 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
35323 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
35324 x = x + 1; /* -1..1 to 0..2 */
35325 x = x * 127.5f; /* 0..2 to 0..255 */
35327 dst_u8[i] = (ma_uint8)x;
35331 static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35333 ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
35336 #if defined(MA_SUPPORT_SSE2)
35337 static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35339 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
35342 #if defined(MA_SUPPORT_AVX2)
35343 static MA_INLINE void ma_pcm_f32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35345 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
35348 #if defined(MA_SUPPORT_NEON)
35349 static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35351 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
35355 MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35357 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35358 ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
35360 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35361 if (ma_has_avx2()) {
35362 ma_pcm_f32_to_u8__avx2(dst, src, count, ditherMode);
35364 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35365 if (ma_has_sse2()) {
35366 ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode);
35368 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35369 if (ma_has_neon()) {
35370 ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode);
35374 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
35379 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35380 static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35384 ma_int16* dst_s16 = (ma_int16*)dst;
35385 const float* src_f32 = (const float*)src;
35387 float ditherMin = 0;
35388 float ditherMax = 0;
35389 if (ditherMode != ma_dither_mode_none) {
35390 ditherMin = 1.0f / -32768;
35391 ditherMax = 1.0f / 32767;
35394 for (i = 0; i < count; i += 1) {
35395 float x = src_f32[i];
35396 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
35397 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
35400 /* The accurate way. */
35401 x = x + 1; /* -1..1 to 0..2 */
35402 x = x * 32767.5f; /* 0..2 to 0..65535 */
35403 x = x - 32768.0f; /* 0...65535 to -32768..32767 */
35405 /* The fast way. */
35406 x = x * 32767.0f; /* -1..1 to -32767..32767 */
35409 dst_s16[i] = (ma_int16)x;
35413 static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35419 ma_int16* dst_s16 = (ma_int16*)dst;
35420 const float* src_f32 = (const float*)src;
35422 float ditherMin = 0;
35423 float ditherMax = 0;
35424 if (ditherMode != ma_dither_mode_none) {
35425 ditherMin = 1.0f / -32768;
35426 ditherMax = 1.0f / 32767;
35431 count4 = count >> 2;
35432 for (i4 = 0; i4 < count4; i4 += 1) {
35433 float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
35434 float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
35435 float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
35436 float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
35438 float x0 = src_f32[i+0];
35439 float x1 = src_f32[i+1];
35440 float x2 = src_f32[i+2];
35441 float x3 = src_f32[i+3];
35448 x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
35449 x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
35450 x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
35451 x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
35453 x0 = x0 * 32767.0f;
35454 x1 = x1 * 32767.0f;
35455 x2 = x2 * 32767.0f;
35456 x3 = x3 * 32767.0f;
35458 dst_s16[i+0] = (ma_int16)x0;
35459 dst_s16[i+1] = (ma_int16)x1;
35460 dst_s16[i+2] = (ma_int16)x2;
35461 dst_s16[i+3] = (ma_int16)x3;
35467 for (; i < count; i += 1) {
35468 float x = src_f32[i];
35469 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
35470 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
35471 x = x * 32767.0f; /* -1..1 to -32767..32767 */
35473 dst_s16[i] = (ma_int16)x;
35477 #if defined(MA_SUPPORT_SSE2)
35478 static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35484 const float* src_f32;
35488 /* Both the input and output buffers need to be aligned to 16 bytes. */
35489 if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
35490 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
35494 dst_s16 = (ma_int16*)dst;
35495 src_f32 = (const float*)src;
35499 if (ditherMode != ma_dither_mode_none) {
35500 ditherMin = 1.0f / -32768;
35501 ditherMax = 1.0f / 32767;
35506 /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
35507 count8 = count >> 3;
35508 for (i8 = 0; i8 < count8; i8 += 1) {
35514 if (ditherMode == ma_dither_mode_none) {
35515 d0 = _mm_set1_ps(0);
35516 d1 = _mm_set1_ps(0);
35517 } else if (ditherMode == ma_dither_mode_rectangle) {
35519 ma_dither_f32_rectangle(ditherMin, ditherMax),
35520 ma_dither_f32_rectangle(ditherMin, ditherMax),
35521 ma_dither_f32_rectangle(ditherMin, ditherMax),
35522 ma_dither_f32_rectangle(ditherMin, ditherMax)
35525 ma_dither_f32_rectangle(ditherMin, ditherMax),
35526 ma_dither_f32_rectangle(ditherMin, ditherMax),
35527 ma_dither_f32_rectangle(ditherMin, ditherMax),
35528 ma_dither_f32_rectangle(ditherMin, ditherMax)
35532 ma_dither_f32_triangle(ditherMin, ditherMax),
35533 ma_dither_f32_triangle(ditherMin, ditherMax),
35534 ma_dither_f32_triangle(ditherMin, ditherMax),
35535 ma_dither_f32_triangle(ditherMin, ditherMax)
35538 ma_dither_f32_triangle(ditherMin, ditherMax),
35539 ma_dither_f32_triangle(ditherMin, ditherMax),
35540 ma_dither_f32_triangle(ditherMin, ditherMax),
35541 ma_dither_f32_triangle(ditherMin, ditherMax)
35545 x0 = *((__m128*)(src_f32 + i) + 0);
35546 x1 = *((__m128*)(src_f32 + i) + 1);
35548 x0 = _mm_add_ps(x0, d0);
35549 x1 = _mm_add_ps(x1, d1);
35551 x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f));
35552 x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f));
35554 _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1)));
35561 for (; i < count; i += 1) {
35562 float x = src_f32[i];
35563 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
35564 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
35565 x = x * 32767.0f; /* -1..1 to -32767..32767 */
35567 dst_s16[i] = (ma_int16)x;
35572 #if defined(MA_SUPPORT_AVX2)
35573 static MA_INLINE void ma_pcm_f32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35579 const float* src_f32;
35583 /* Both the input and output buffers need to be aligned to 32 bytes. */
35584 if ((((ma_uintptr)dst & 31) != 0) || (((ma_uintptr)src & 31) != 0)) {
35585 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
35589 dst_s16 = (ma_int16*)dst;
35590 src_f32 = (const float*)src;
35594 if (ditherMode != ma_dither_mode_none) {
35595 ditherMin = 1.0f / -32768;
35596 ditherMax = 1.0f / 32767;
35601 /* AVX2. AVX2 allows us to output 16 s16's at a time which means our loop is unrolled 16 times. */
35602 count16 = count >> 4;
35603 for (i16 = 0; i16 < count16; i16 += 1) {
35614 if (ditherMode == ma_dither_mode_none) {
35615 d0 = _mm256_set1_ps(0);
35616 d1 = _mm256_set1_ps(0);
35617 } else if (ditherMode == ma_dither_mode_rectangle) {
35618 d0 = _mm256_set_ps(
35619 ma_dither_f32_rectangle(ditherMin, ditherMax),
35620 ma_dither_f32_rectangle(ditherMin, ditherMax),
35621 ma_dither_f32_rectangle(ditherMin, ditherMax),
35622 ma_dither_f32_rectangle(ditherMin, ditherMax),
35623 ma_dither_f32_rectangle(ditherMin, ditherMax),
35624 ma_dither_f32_rectangle(ditherMin, ditherMax),
35625 ma_dither_f32_rectangle(ditherMin, ditherMax),
35626 ma_dither_f32_rectangle(ditherMin, ditherMax)
35628 d1 = _mm256_set_ps(
35629 ma_dither_f32_rectangle(ditherMin, ditherMax),
35630 ma_dither_f32_rectangle(ditherMin, ditherMax),
35631 ma_dither_f32_rectangle(ditherMin, ditherMax),
35632 ma_dither_f32_rectangle(ditherMin, ditherMax),
35633 ma_dither_f32_rectangle(ditherMin, ditherMax),
35634 ma_dither_f32_rectangle(ditherMin, ditherMax),
35635 ma_dither_f32_rectangle(ditherMin, ditherMax),
35636 ma_dither_f32_rectangle(ditherMin, ditherMax)
35639 d0 = _mm256_set_ps(
35640 ma_dither_f32_triangle(ditherMin, ditherMax),
35641 ma_dither_f32_triangle(ditherMin, ditherMax),
35642 ma_dither_f32_triangle(ditherMin, ditherMax),
35643 ma_dither_f32_triangle(ditherMin, ditherMax),
35644 ma_dither_f32_triangle(ditherMin, ditherMax),
35645 ma_dither_f32_triangle(ditherMin, ditherMax),
35646 ma_dither_f32_triangle(ditherMin, ditherMax),
35647 ma_dither_f32_triangle(ditherMin, ditherMax)
35649 d1 = _mm256_set_ps(
35650 ma_dither_f32_triangle(ditherMin, ditherMax),
35651 ma_dither_f32_triangle(ditherMin, ditherMax),
35652 ma_dither_f32_triangle(ditherMin, ditherMax),
35653 ma_dither_f32_triangle(ditherMin, ditherMax),
35654 ma_dither_f32_triangle(ditherMin, ditherMax),
35655 ma_dither_f32_triangle(ditherMin, ditherMax),
35656 ma_dither_f32_triangle(ditherMin, ditherMax),
35657 ma_dither_f32_triangle(ditherMin, ditherMax)
35661 x0 = *((__m256*)(src_f32 + i) + 0);
35662 x1 = *((__m256*)(src_f32 + i) + 1);
35664 x0 = _mm256_add_ps(x0, d0);
35665 x1 = _mm256_add_ps(x1, d1);
35667 x0 = _mm256_mul_ps(x0, _mm256_set1_ps(32767.0f));
35668 x1 = _mm256_mul_ps(x1, _mm256_set1_ps(32767.0f));
35670 /* Computing the final result is a little more complicated for AVX2 than SSE2. */
35671 i0 = _mm256_cvttps_epi32(x0);
35672 i1 = _mm256_cvttps_epi32(x1);
35673 p0 = _mm256_permute2x128_si256(i0, i1, 0 | 32);
35674 p1 = _mm256_permute2x128_si256(i0, i1, 1 | 48);
35675 r = _mm256_packs_epi32(p0, p1);
35677 _mm256_stream_si256(((__m256i*)(dst_s16 + i)), r);
35684 for (; i < count; i += 1) {
35685 float x = src_f32[i];
35686 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
35687 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
35688 x = x * 32767.0f; /* -1..1 to -32767..32767 */
35690 dst_s16[i] = (ma_int16)x;
35695 #if defined(MA_SUPPORT_NEON)
35696 static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35702 const float* src_f32;
35706 if (!ma_has_neon()) {
35707 return ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
35710 /* Both the input and output buffers need to be aligned to 16 bytes. */
35711 if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
35712 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
35716 dst_s16 = (ma_int16*)dst;
35717 src_f32 = (const float*)src;
35721 if (ditherMode != ma_dither_mode_none) {
35722 ditherMin = 1.0f / -32768;
35723 ditherMax = 1.0f / 32767;
35728 /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
35729 count8 = count >> 3;
35730 for (i8 = 0; i8 < count8; i8 += 1) {
35738 if (ditherMode == ma_dither_mode_none) {
35739 d0 = vmovq_n_f32(0);
35740 d1 = vmovq_n_f32(0);
35741 } else if (ditherMode == ma_dither_mode_rectangle) {
35743 d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
35744 d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
35745 d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
35746 d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
35747 d0 = vld1q_f32(d0v);
35750 d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
35751 d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
35752 d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
35753 d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
35754 d1 = vld1q_f32(d1v);
35757 d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
35758 d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
35759 d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
35760 d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
35761 d0 = vld1q_f32(d0v);
35764 d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
35765 d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
35766 d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
35767 d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
35768 d1 = vld1q_f32(d1v);
35771 x0 = *((float32x4_t*)(src_f32 + i) + 0);
35772 x1 = *((float32x4_t*)(src_f32 + i) + 1);
35774 x0 = vaddq_f32(x0, d0);
35775 x1 = vaddq_f32(x1, d1);
35777 x0 = vmulq_n_f32(x0, 32767.0f);
35778 x1 = vmulq_n_f32(x1, 32767.0f);
35780 i0 = vcvtq_s32_f32(x0);
35781 i1 = vcvtq_s32_f32(x1);
35782 *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1));
35789 for (; i < count; i += 1) {
35790 float x = src_f32[i];
35791 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
35792 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
35793 x = x * 32767.0f; /* -1..1 to -32767..32767 */
35795 dst_s16[i] = (ma_int16)x;
35799 #endif /* MA_USE_REFERENCE_CONVERSION_APIS */
35801 MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35803 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35804 ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode);
35806 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35807 if (ma_has_avx2()) {
35808 ma_pcm_f32_to_s16__avx2(dst, src, count, ditherMode);
35810 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35811 if (ma_has_sse2()) {
35812 ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode);
35814 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35815 if (ma_has_neon()) {
35816 ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode);
35820 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
35826 static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35828 ma_uint8* dst_s24 = (ma_uint8*)dst;
35829 const float* src_f32 = (const float*)src;
35832 for (i = 0; i < count; i += 1) {
35834 float x = src_f32[i];
35835 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
35838 /* The accurate way. */
35839 x = x + 1; /* -1..1 to 0..2 */
35840 x = x * 8388607.5f; /* 0..2 to 0..16777215 */
35841 x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */
35843 /* The fast way. */
35844 x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */
35848 dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0);
35849 dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8);
35850 dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16);
35853 (void)ditherMode; /* No dithering for f32 -> s24. */
35856 static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35858 ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
35861 #if defined(MA_SUPPORT_SSE2)
35862 static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35864 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
35867 #if defined(MA_SUPPORT_AVX2)
35868 static MA_INLINE void ma_pcm_f32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35870 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
35873 #if defined(MA_SUPPORT_NEON)
35874 static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35876 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
35880 MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35882 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35883 ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
35885 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35886 if (ma_has_avx2()) {
35887 ma_pcm_f32_to_s24__avx2(dst, src, count, ditherMode);
35889 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35890 if (ma_has_sse2()) {
35891 ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode);
35893 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35894 if (ma_has_neon()) {
35895 ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode);
35899 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
35905 static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35907 ma_int32* dst_s32 = (ma_int32*)dst;
35908 const float* src_f32 = (const float*)src;
35911 for (i = 0; i < count; i += 1) {
35912 double x = src_f32[i];
35913 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
35916 /* The accurate way. */
35917 x = x + 1; /* -1..1 to 0..2 */
35918 x = x * 2147483647.5; /* 0..2 to 0..4294967295 */
35919 x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */
35921 /* The fast way. */
35922 x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */
35925 dst_s32[i] = (ma_int32)x;
35928 (void)ditherMode; /* No dithering for f32 -> s32. */
35931 static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35933 ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
35936 #if defined(MA_SUPPORT_SSE2)
35937 static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35939 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
35942 #if defined(MA_SUPPORT_AVX2)
35943 static MA_INLINE void ma_pcm_f32_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35945 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
35948 #if defined(MA_SUPPORT_NEON)
35949 static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35951 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
35955 MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35957 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35958 ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
35960 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35961 if (ma_has_avx2()) {
35962 ma_pcm_f32_to_s32__avx2(dst, src, count, ditherMode);
35964 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35965 if (ma_has_sse2()) {
35966 ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode);
35968 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35969 if (ma_has_neon()) {
35970 ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode);
35974 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
35980 MA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35984 ma_copy_memory_64(dst, src, count * sizeof(float));
35988 static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
35990 float* dst_f32 = (float*)dst;
35991 const float** src_f32 = (const float**)src;
35994 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
35995 ma_uint32 iChannel;
35996 for (iChannel = 0; iChannel < channels; iChannel += 1) {
35997 dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame];
36002 static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
36004 ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
36007 MA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
36009 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36010 ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
36012 ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels);
36017 static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
36019 float** dst_f32 = (float**)dst;
36020 const float* src_f32 = (const float*)src;
36023 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
36024 ma_uint32 iChannel;
36025 for (iChannel = 0; iChannel < channels; iChannel += 1) {
36026 dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel];
36031 static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
36033 ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
36036 MA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
36038 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36039 ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
36041 ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels);
36046 MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
36048 if (formatOut == formatIn) {
36049 ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut));
36059 case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return;
36060 case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return;
36061 case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return;
36062 case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return;
36067 case ma_format_s16:
36071 case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return;
36072 case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return;
36073 case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return;
36074 case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return;
36079 case ma_format_s24:
36083 case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return;
36084 case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return;
36085 case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return;
36086 case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return;
36091 case ma_format_s32:
36095 case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
36096 case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
36097 case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
36098 case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return;
36103 case ma_format_f32:
36107 case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
36108 case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
36109 case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
36110 case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return;
36119 MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode)
36121 ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode);
36124 MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)
36126 if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) {
36127 return; /* Invalid args. */
36130 /* For efficiency we do this per format. */
36132 case ma_format_s16:
36134 const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames;
36135 ma_uint64 iPCMFrame;
36136 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
36137 ma_uint32 iChannel;
36138 for (iChannel = 0; iChannel < channels; ++iChannel) {
36139 ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel];
36140 pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel];
36145 case ma_format_f32:
36147 const float* pSrcF32 = (const float*)pInterleavedPCMFrames;
36148 ma_uint64 iPCMFrame;
36149 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
36150 ma_uint32 iChannel;
36151 for (iChannel = 0; iChannel < channels; ++iChannel) {
36152 float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel];
36153 pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel];
36160 ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
36161 ma_uint64 iPCMFrame;
36162 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
36163 ma_uint32 iChannel;
36164 for (iChannel = 0; iChannel < channels; ++iChannel) {
36165 void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
36166 const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
36167 memcpy(pDst, pSrc, sampleSizeInBytes);
36174 MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)
36178 case ma_format_s16:
36180 ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames;
36181 ma_uint64 iPCMFrame;
36182 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
36183 ma_uint32 iChannel;
36184 for (iChannel = 0; iChannel < channels; ++iChannel) {
36185 const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel];
36186 pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame];
36191 case ma_format_f32:
36193 float* pDstF32 = (float*)pInterleavedPCMFrames;
36194 ma_uint64 iPCMFrame;
36195 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
36196 ma_uint32 iChannel;
36197 for (iChannel = 0; iChannel < channels; ++iChannel) {
36198 const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel];
36199 pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame];
36206 ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
36207 ma_uint64 iPCMFrame;
36208 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
36209 ma_uint32 iChannel;
36210 for (iChannel = 0; iChannel < channels; ++iChannel) {
36211 void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
36212 const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
36213 memcpy(pDst, pSrc, sampleSizeInBytes);
36221 /**************************************************************************************************************************************************************
36225 **************************************************************************************************************************************************************/
36226 #ifndef MA_BIQUAD_FIXED_POINT_SHIFT
36227 #define MA_BIQUAD_FIXED_POINT_SHIFT 14
36230 static ma_int32 ma_biquad_float_to_fp(double x)
36232 return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT));
36235 MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2)
36237 ma_biquad_config config;
36239 MA_ZERO_OBJECT(&config);
36240 config.format = format;
36241 config.channels = channels;
36252 MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, ma_biquad* pBQ)
36255 return MA_INVALID_ARGS;
36258 MA_ZERO_OBJECT(pBQ);
36260 if (pConfig == NULL) {
36261 return MA_INVALID_ARGS;
36264 if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) {
36265 return MA_INVALID_ARGS;
36268 return ma_biquad_reinit(pConfig, pBQ);
36271 MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ)
36273 if (pBQ == NULL || pConfig == NULL) {
36274 return MA_INVALID_ARGS;
36277 if (pConfig->a0 == 0) {
36278 return MA_INVALID_ARGS; /* Division by zero. */
36281 /* Only supporting f32 and s16. */
36282 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
36283 return MA_INVALID_ARGS;
36286 /* The format cannot be changed after initialization. */
36287 if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) {
36288 return MA_INVALID_OPERATION;
36291 /* The channel count cannot be changed after initialization. */
36292 if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) {
36293 return MA_INVALID_OPERATION;
36297 pBQ->format = pConfig->format;
36298 pBQ->channels = pConfig->channels;
36301 if (pConfig->format == ma_format_f32) {
36302 pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0);
36303 pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0);
36304 pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0);
36305 pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0);
36306 pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0);
36308 pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0);
36309 pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0);
36310 pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0);
36311 pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0);
36312 pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0);
36318 static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX)
36321 const ma_uint32 channels = pBQ->channels;
36322 const float b0 = pBQ->b0.f32;
36323 const float b1 = pBQ->b1.f32;
36324 const float b2 = pBQ->b2.f32;
36325 const float a1 = pBQ->a1.f32;
36326 const float a2 = pBQ->a2.f32;
36328 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
36329 for (c = 0; c < channels; c += 1) {
36330 float r1 = pBQ->r1[c].f32;
36331 float r2 = pBQ->r2[c].f32;
36336 r1 = b1*x - a1*y + r2;
36340 pBQ->r1[c].f32 = r1;
36341 pBQ->r2[c].f32 = r2;
36345 static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX)
36347 ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
36350 static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
36353 const ma_uint32 channels = pBQ->channels;
36354 const ma_int32 b0 = pBQ->b0.s32;
36355 const ma_int32 b1 = pBQ->b1.s32;
36356 const ma_int32 b2 = pBQ->b2.s32;
36357 const ma_int32 a1 = pBQ->a1.s32;
36358 const ma_int32 a2 = pBQ->a2.s32;
36360 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
36361 for (c = 0; c < channels; c += 1) {
36362 ma_int32 r1 = pBQ->r1[c].s32;
36363 ma_int32 r2 = pBQ->r2[c].s32;
36364 ma_int32 x = pX[c];
36367 y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
36368 r1 = (b1*x - a1*y + r2);
36369 r2 = (b2*x - a2*y);
36371 pY[c] = (ma_int16)ma_clamp(y, -32768, 32767);
36372 pBQ->r1[c].s32 = r1;
36373 pBQ->r2[c].s32 = r2;
36377 static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
36379 ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
36382 MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
36386 if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) {
36387 return MA_INVALID_ARGS;
36390 /* 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. */
36392 if (pBQ->format == ma_format_f32) {
36393 /* */ float* pY = ( float*)pFramesOut;
36394 const float* pX = (const float*)pFramesIn;
36396 for (n = 0; n < frameCount; n += 1) {
36397 ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
36398 pY += pBQ->channels;
36399 pX += pBQ->channels;
36401 } else if (pBQ->format == ma_format_s16) {
36402 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
36403 const ma_int16* pX = (const ma_int16*)pFramesIn;
36405 for (n = 0; n < frameCount; n += 1) {
36406 ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
36407 pY += pBQ->channels;
36408 pX += pBQ->channels;
36411 MA_ASSERT(MA_FALSE);
36412 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
36418 MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ)
36428 /**************************************************************************************************************************************************************
36432 **************************************************************************************************************************************************************/
36433 MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
36435 ma_lpf1_config config;
36437 MA_ZERO_OBJECT(&config);
36438 config.format = format;
36439 config.channels = channels;
36440 config.sampleRate = sampleRate;
36441 config.cutoffFrequency = cutoffFrequency;
36447 MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
36449 ma_lpf2_config config;
36451 MA_ZERO_OBJECT(&config);
36452 config.format = format;
36453 config.channels = channels;
36454 config.sampleRate = sampleRate;
36455 config.cutoffFrequency = cutoffFrequency;
36458 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
36459 if (config.q == 0) {
36460 config.q = 0.707107;
36467 MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, ma_lpf1* pLPF)
36469 if (pLPF == NULL) {
36470 return MA_INVALID_ARGS;
36473 MA_ZERO_OBJECT(pLPF);
36475 if (pConfig == NULL) {
36476 return MA_INVALID_ARGS;
36479 if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) {
36480 return MA_INVALID_ARGS;
36483 return ma_lpf1_reinit(pConfig, pLPF);
36486 MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF)
36490 if (pLPF == NULL || pConfig == NULL) {
36491 return MA_INVALID_ARGS;
36494 /* Only supporting f32 and s16. */
36495 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
36496 return MA_INVALID_ARGS;
36499 /* The format cannot be changed after initialization. */
36500 if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {
36501 return MA_INVALID_OPERATION;
36504 /* The channel count cannot be changed after initialization. */
36505 if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {
36506 return MA_INVALID_OPERATION;
36509 pLPF->format = pConfig->format;
36510 pLPF->channels = pConfig->channels;
36512 a = ma_exp(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);
36513 if (pConfig->format == ma_format_f32) {
36514 pLPF->a.f32 = (float)a;
36516 pLPF->a.s32 = ma_biquad_float_to_fp(a);
36522 static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX)
36525 const ma_uint32 channels = pLPF->channels;
36526 const float a = pLPF->a.f32;
36527 const float b = 1 - a;
36529 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
36530 for (c = 0; c < channels; c += 1) {
36531 float r1 = pLPF->r1[c].f32;
36538 pLPF->r1[c].f32 = y;
36542 static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX)
36545 const ma_uint32 channels = pLPF->channels;
36546 const ma_int32 a = pLPF->a.s32;
36547 const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);
36549 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
36550 for (c = 0; c < channels; c += 1) {
36551 ma_int32 r1 = pLPF->r1[c].s32;
36552 ma_int32 x = pX[c];
36555 y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
36557 pY[c] = (ma_int16)y;
36558 pLPF->r1[c].s32 = (ma_int32)y;
36562 MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
36566 if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {
36567 return MA_INVALID_ARGS;
36570 /* 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. */
36572 if (pLPF->format == ma_format_f32) {
36573 /* */ float* pY = ( float*)pFramesOut;
36574 const float* pX = (const float*)pFramesIn;
36576 for (n = 0; n < frameCount; n += 1) {
36577 ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX);
36578 pY += pLPF->channels;
36579 pX += pLPF->channels;
36581 } else if (pLPF->format == ma_format_s16) {
36582 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
36583 const ma_int16* pX = (const ma_int16*)pFramesIn;
36585 for (n = 0; n < frameCount; n += 1) {
36586 ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX);
36587 pY += pLPF->channels;
36588 pX += pLPF->channels;
36591 MA_ASSERT(MA_FALSE);
36592 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
36598 MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF)
36600 if (pLPF == NULL) {
36608 static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig)
36610 ma_biquad_config bqConfig;
36617 MA_ASSERT(pConfig != NULL);
36620 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
36625 bqConfig.b0 = (1 - c) / 2;
36626 bqConfig.b1 = 1 - c;
36627 bqConfig.b2 = (1 - c) / 2;
36628 bqConfig.a0 = 1 + a;
36629 bqConfig.a1 = -2 * c;
36630 bqConfig.a2 = 1 - a;
36632 bqConfig.format = pConfig->format;
36633 bqConfig.channels = pConfig->channels;
36638 MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, ma_lpf2* pLPF)
36641 ma_biquad_config bqConfig;
36643 if (pLPF == NULL) {
36644 return MA_INVALID_ARGS;
36647 MA_ZERO_OBJECT(pLPF);
36649 if (pConfig == NULL) {
36650 return MA_INVALID_ARGS;
36653 bqConfig = ma_lpf2__get_biquad_config(pConfig);
36654 result = ma_biquad_init(&bqConfig, &pLPF->bq);
36655 if (result != MA_SUCCESS) {
36662 MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF)
36665 ma_biquad_config bqConfig;
36667 if (pLPF == NULL || pConfig == NULL) {
36668 return MA_INVALID_ARGS;
36671 bqConfig = ma_lpf2__get_biquad_config(pConfig);
36672 result = ma_biquad_reinit(&bqConfig, &pLPF->bq);
36673 if (result != MA_SUCCESS) {
36680 static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
36682 ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn);
36685 static MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn)
36687 ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn);
36690 MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
36692 if (pLPF == NULL) {
36693 return MA_INVALID_ARGS;
36696 return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount);
36699 MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF)
36701 if (pLPF == NULL) {
36705 return ma_biquad_get_latency(&pLPF->bq);
36709 MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
36711 ma_lpf_config config;
36713 MA_ZERO_OBJECT(&config);
36714 config.format = format;
36715 config.channels = channels;
36716 config.sampleRate = sampleRate;
36717 config.cutoffFrequency = cutoffFrequency;
36718 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
36723 static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, ma_lpf* pLPF, ma_bool32 isNew)
36726 ma_uint32 lpf1Count;
36727 ma_uint32 lpf2Count;
36731 if (pLPF == NULL || pConfig == NULL) {
36732 return MA_INVALID_ARGS;
36735 /* Only supporting f32 and s16. */
36736 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
36737 return MA_INVALID_ARGS;
36740 /* The format cannot be changed after initialization. */
36741 if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {
36742 return MA_INVALID_OPERATION;
36745 /* The channel count cannot be changed after initialization. */
36746 if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {
36747 return MA_INVALID_OPERATION;
36750 if (pConfig->order > MA_MAX_FILTER_ORDER) {
36751 return MA_INVALID_ARGS;
36754 lpf1Count = pConfig->order % 2;
36755 lpf2Count = pConfig->order / 2;
36757 MA_ASSERT(lpf1Count <= ma_countof(pLPF->lpf1));
36758 MA_ASSERT(lpf2Count <= ma_countof(pLPF->lpf2));
36760 /* The filter order can't change between reinits. */
36762 if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) {
36763 return MA_INVALID_OPERATION;
36767 for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) {
36768 ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
36771 result = ma_lpf1_init(&lpf1Config, &pLPF->lpf1[ilpf1]);
36773 result = ma_lpf1_reinit(&lpf1Config, &pLPF->lpf1[ilpf1]);
36776 if (result != MA_SUCCESS) {
36781 for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) {
36782 ma_lpf2_config lpf2Config;
36786 /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */
36787 if (lpf1Count == 1) {
36788 a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */
36790 a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */
36792 q = 1 / (2*ma_cos(a));
36794 lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
36797 result = ma_lpf2_init(&lpf2Config, &pLPF->lpf2[ilpf2]);
36799 result = ma_lpf2_reinit(&lpf2Config, &pLPF->lpf2[ilpf2]);
36802 if (result != MA_SUCCESS) {
36807 pLPF->lpf1Count = lpf1Count;
36808 pLPF->lpf2Count = lpf2Count;
36809 pLPF->format = pConfig->format;
36810 pLPF->channels = pConfig->channels;
36811 pLPF->sampleRate = pConfig->sampleRate;
36816 MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF)
36818 if (pLPF == NULL) {
36819 return MA_INVALID_ARGS;
36822 MA_ZERO_OBJECT(pLPF);
36824 if (pConfig == NULL) {
36825 return MA_INVALID_ARGS;
36828 return ma_lpf_reinit__internal(pConfig, pLPF, /*isNew*/MA_TRUE);
36831 MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF)
36833 return ma_lpf_reinit__internal(pConfig, pLPF, /*isNew*/MA_FALSE);
36836 static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX)
36841 MA_ASSERT(pLPF->format == ma_format_f32);
36843 MA_COPY_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));
36845 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
36846 ma_lpf1_process_pcm_frame_f32(&pLPF->lpf1[ilpf1], pY, pY);
36849 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
36850 ma_lpf2_process_pcm_frame_f32(&pLPF->lpf2[ilpf2], pY, pY);
36854 static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX)
36859 MA_ASSERT(pLPF->format == ma_format_s16);
36861 MA_COPY_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));
36863 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
36864 ma_lpf1_process_pcm_frame_s16(&pLPF->lpf1[ilpf1], pY, pY);
36867 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
36868 ma_lpf2_process_pcm_frame_s16(&pLPF->lpf2[ilpf2], pY, pY);
36872 MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
36878 if (pLPF == NULL) {
36879 return MA_INVALID_ARGS;
36882 /* Faster path for in-place. */
36883 if (pFramesOut == pFramesIn) {
36884 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
36885 result = ma_lpf1_process_pcm_frames(&pLPF->lpf1[ilpf1], pFramesOut, pFramesOut, frameCount);
36886 if (result != MA_SUCCESS) {
36891 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
36892 result = ma_lpf2_process_pcm_frames(&pLPF->lpf2[ilpf2], pFramesOut, pFramesOut, frameCount);
36893 if (result != MA_SUCCESS) {
36899 /* Slightly slower path for copying. */
36900 if (pFramesOut != pFramesIn) {
36903 /* */ if (pLPF->format == ma_format_f32) {
36904 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
36905 const float* pFramesInF32 = (const float*)pFramesIn;
36907 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
36908 ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32);
36909 pFramesOutF32 += pLPF->channels;
36910 pFramesInF32 += pLPF->channels;
36912 } else if (pLPF->format == ma_format_s16) {
36913 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
36914 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
36916 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
36917 ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16);
36918 pFramesOutS16 += pLPF->channels;
36919 pFramesInS16 += pLPF->channels;
36922 MA_ASSERT(MA_FALSE);
36923 return MA_INVALID_OPERATION; /* Should never hit this. */
36930 MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF)
36932 if (pLPF == NULL) {
36936 return pLPF->lpf2Count*2 + pLPF->lpf1Count;
36940 /**************************************************************************************************************************************************************
36942 High-Pass Filtering
36944 **************************************************************************************************************************************************************/
36945 MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
36947 ma_hpf1_config config;
36949 MA_ZERO_OBJECT(&config);
36950 config.format = format;
36951 config.channels = channels;
36952 config.sampleRate = sampleRate;
36953 config.cutoffFrequency = cutoffFrequency;
36958 MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
36960 ma_hpf2_config config;
36962 MA_ZERO_OBJECT(&config);
36963 config.format = format;
36964 config.channels = channels;
36965 config.sampleRate = sampleRate;
36966 config.cutoffFrequency = cutoffFrequency;
36969 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
36970 if (config.q == 0) {
36971 config.q = 0.707107;
36978 MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, ma_hpf1* pHPF)
36980 if (pHPF == NULL) {
36981 return MA_INVALID_ARGS;
36984 MA_ZERO_OBJECT(pHPF);
36986 if (pConfig == NULL) {
36987 return MA_INVALID_ARGS;
36990 if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) {
36991 return MA_INVALID_ARGS;
36994 return ma_hpf1_reinit(pConfig, pHPF);
36997 MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF)
37001 if (pHPF == NULL || pConfig == NULL) {
37002 return MA_INVALID_ARGS;
37005 /* Only supporting f32 and s16. */
37006 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
37007 return MA_INVALID_ARGS;
37010 /* The format cannot be changed after initialization. */
37011 if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {
37012 return MA_INVALID_OPERATION;
37015 /* The channel count cannot be changed after initialization. */
37016 if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {
37017 return MA_INVALID_OPERATION;
37020 pHPF->format = pConfig->format;
37021 pHPF->channels = pConfig->channels;
37023 a = ma_exp(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);
37024 if (pConfig->format == ma_format_f32) {
37025 pHPF->a.f32 = (float)a;
37027 pHPF->a.s32 = ma_biquad_float_to_fp(a);
37033 static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX)
37036 const ma_uint32 channels = pHPF->channels;
37037 const float a = 1 - pHPF->a.f32;
37038 const float b = 1 - a;
37040 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
37041 for (c = 0; c < channels; c += 1) {
37042 float r1 = pHPF->r1[c].f32;
37049 pHPF->r1[c].f32 = y;
37053 static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX)
37056 const ma_uint32 channels = pHPF->channels;
37057 const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32);
37058 const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);
37060 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
37061 for (c = 0; c < channels; c += 1) {
37062 ma_int32 r1 = pHPF->r1[c].s32;
37063 ma_int32 x = pX[c];
37066 y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
37068 pY[c] = (ma_int16)y;
37069 pHPF->r1[c].s32 = (ma_int32)y;
37073 MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
37077 if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {
37078 return MA_INVALID_ARGS;
37081 /* 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. */
37083 if (pHPF->format == ma_format_f32) {
37084 /* */ float* pY = ( float*)pFramesOut;
37085 const float* pX = (const float*)pFramesIn;
37087 for (n = 0; n < frameCount; n += 1) {
37088 ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX);
37089 pY += pHPF->channels;
37090 pX += pHPF->channels;
37092 } else if (pHPF->format == ma_format_s16) {
37093 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
37094 const ma_int16* pX = (const ma_int16*)pFramesIn;
37096 for (n = 0; n < frameCount; n += 1) {
37097 ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX);
37098 pY += pHPF->channels;
37099 pX += pHPF->channels;
37102 MA_ASSERT(MA_FALSE);
37103 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
37109 MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF)
37111 if (pHPF == NULL) {
37119 static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig)
37121 ma_biquad_config bqConfig;
37128 MA_ASSERT(pConfig != NULL);
37131 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
37136 bqConfig.b0 = (1 + c) / 2;
37137 bqConfig.b1 = -(1 + c);
37138 bqConfig.b2 = (1 + c) / 2;
37139 bqConfig.a0 = 1 + a;
37140 bqConfig.a1 = -2 * c;
37141 bqConfig.a2 = 1 - a;
37143 bqConfig.format = pConfig->format;
37144 bqConfig.channels = pConfig->channels;
37149 MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, ma_hpf2* pHPF)
37152 ma_biquad_config bqConfig;
37154 if (pHPF == NULL) {
37155 return MA_INVALID_ARGS;
37158 MA_ZERO_OBJECT(pHPF);
37160 if (pConfig == NULL) {
37161 return MA_INVALID_ARGS;
37164 bqConfig = ma_hpf2__get_biquad_config(pConfig);
37165 result = ma_biquad_init(&bqConfig, &pHPF->bq);
37166 if (result != MA_SUCCESS) {
37173 MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF)
37176 ma_biquad_config bqConfig;
37178 if (pHPF == NULL || pConfig == NULL) {
37179 return MA_INVALID_ARGS;
37182 bqConfig = ma_hpf2__get_biquad_config(pConfig);
37183 result = ma_biquad_reinit(&bqConfig, &pHPF->bq);
37184 if (result != MA_SUCCESS) {
37191 static MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
37193 ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn);
37196 static MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn)
37198 ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn);
37201 MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
37203 if (pHPF == NULL) {
37204 return MA_INVALID_ARGS;
37207 return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount);
37210 MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF)
37212 if (pHPF == NULL) {
37216 return ma_biquad_get_latency(&pHPF->bq);
37220 MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
37222 ma_hpf_config config;
37224 MA_ZERO_OBJECT(&config);
37225 config.format = format;
37226 config.channels = channels;
37227 config.sampleRate = sampleRate;
37228 config.cutoffFrequency = cutoffFrequency;
37229 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
37234 static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, ma_hpf* pHPF, ma_bool32 isNew)
37237 ma_uint32 hpf1Count;
37238 ma_uint32 hpf2Count;
37242 if (pHPF == NULL || pConfig == NULL) {
37243 return MA_INVALID_ARGS;
37246 /* Only supporting f32 and s16. */
37247 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
37248 return MA_INVALID_ARGS;
37251 /* The format cannot be changed after initialization. */
37252 if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {
37253 return MA_INVALID_OPERATION;
37256 /* The channel count cannot be changed after initialization. */
37257 if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {
37258 return MA_INVALID_OPERATION;
37261 if (pConfig->order > MA_MAX_FILTER_ORDER) {
37262 return MA_INVALID_ARGS;
37265 hpf1Count = pConfig->order % 2;
37266 hpf2Count = pConfig->order / 2;
37268 MA_ASSERT(hpf1Count <= ma_countof(pHPF->hpf1));
37269 MA_ASSERT(hpf2Count <= ma_countof(pHPF->hpf2));
37271 /* The filter order can't change between reinits. */
37273 if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) {
37274 return MA_INVALID_OPERATION;
37278 for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) {
37279 ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
37282 result = ma_hpf1_init(&hpf1Config, &pHPF->hpf1[ihpf1]);
37284 result = ma_hpf1_reinit(&hpf1Config, &pHPF->hpf1[ihpf1]);
37287 if (result != MA_SUCCESS) {
37292 for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) {
37293 ma_hpf2_config hpf2Config;
37297 /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */
37298 if (hpf1Count == 1) {
37299 a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */
37301 a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */
37303 q = 1 / (2*ma_cos(a));
37305 hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
37308 result = ma_hpf2_init(&hpf2Config, &pHPF->hpf2[ihpf2]);
37310 result = ma_hpf2_reinit(&hpf2Config, &pHPF->hpf2[ihpf2]);
37313 if (result != MA_SUCCESS) {
37318 pHPF->hpf1Count = hpf1Count;
37319 pHPF->hpf2Count = hpf2Count;
37320 pHPF->format = pConfig->format;
37321 pHPF->channels = pConfig->channels;
37322 pHPF->sampleRate = pConfig->sampleRate;
37327 MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, ma_hpf* pHPF)
37329 if (pHPF == NULL) {
37330 return MA_INVALID_ARGS;
37333 MA_ZERO_OBJECT(pHPF);
37335 if (pConfig == NULL) {
37336 return MA_INVALID_ARGS;
37339 return ma_hpf_reinit__internal(pConfig, pHPF, /*isNew*/MA_TRUE);
37342 MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF)
37344 return ma_hpf_reinit__internal(pConfig, pHPF, /*isNew*/MA_FALSE);
37347 MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
37353 if (pHPF == NULL) {
37354 return MA_INVALID_ARGS;
37357 /* Faster path for in-place. */
37358 if (pFramesOut == pFramesIn) {
37359 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
37360 result = ma_hpf1_process_pcm_frames(&pHPF->hpf1[ihpf1], pFramesOut, pFramesOut, frameCount);
37361 if (result != MA_SUCCESS) {
37366 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
37367 result = ma_hpf2_process_pcm_frames(&pHPF->hpf2[ihpf2], pFramesOut, pFramesOut, frameCount);
37368 if (result != MA_SUCCESS) {
37374 /* Slightly slower path for copying. */
37375 if (pFramesOut != pFramesIn) {
37378 /* */ if (pHPF->format == ma_format_f32) {
37379 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
37380 const float* pFramesInF32 = (const float*)pFramesIn;
37382 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
37383 MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));
37385 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
37386 ma_hpf1_process_pcm_frame_f32(&pHPF->hpf1[ihpf1], pFramesOutF32, pFramesOutF32);
37389 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
37390 ma_hpf2_process_pcm_frame_f32(&pHPF->hpf2[ihpf2], pFramesOutF32, pFramesOutF32);
37393 pFramesOutF32 += pHPF->channels;
37394 pFramesInF32 += pHPF->channels;
37396 } else if (pHPF->format == ma_format_s16) {
37397 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
37398 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
37400 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
37401 MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));
37403 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
37404 ma_hpf1_process_pcm_frame_s16(&pHPF->hpf1[ihpf1], pFramesOutS16, pFramesOutS16);
37407 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
37408 ma_hpf2_process_pcm_frame_s16(&pHPF->hpf2[ihpf2], pFramesOutS16, pFramesOutS16);
37411 pFramesOutS16 += pHPF->channels;
37412 pFramesInS16 += pHPF->channels;
37415 MA_ASSERT(MA_FALSE);
37416 return MA_INVALID_OPERATION; /* Should never hit this. */
37423 MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF)
37425 if (pHPF == NULL) {
37429 return pHPF->hpf2Count*2 + pHPF->hpf1Count;
37433 /**************************************************************************************************************************************************************
37435 Band-Pass Filtering
37437 **************************************************************************************************************************************************************/
37438 MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
37440 ma_bpf2_config config;
37442 MA_ZERO_OBJECT(&config);
37443 config.format = format;
37444 config.channels = channels;
37445 config.sampleRate = sampleRate;
37446 config.cutoffFrequency = cutoffFrequency;
37449 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
37450 if (config.q == 0) {
37451 config.q = 0.707107;
37458 static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig)
37460 ma_biquad_config bqConfig;
37467 MA_ASSERT(pConfig != NULL);
37470 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
37475 bqConfig.b0 = q * a;
37477 bqConfig.b2 = -q * a;
37478 bqConfig.a0 = 1 + a;
37479 bqConfig.a1 = -2 * c;
37480 bqConfig.a2 = 1 - a;
37482 bqConfig.format = pConfig->format;
37483 bqConfig.channels = pConfig->channels;
37488 MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, ma_bpf2* pBPF)
37491 ma_biquad_config bqConfig;
37493 if (pBPF == NULL) {
37494 return MA_INVALID_ARGS;
37497 MA_ZERO_OBJECT(pBPF);
37499 if (pConfig == NULL) {
37500 return MA_INVALID_ARGS;
37503 bqConfig = ma_bpf2__get_biquad_config(pConfig);
37504 result = ma_biquad_init(&bqConfig, &pBPF->bq);
37505 if (result != MA_SUCCESS) {
37512 MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF)
37515 ma_biquad_config bqConfig;
37517 if (pBPF == NULL || pConfig == NULL) {
37518 return MA_INVALID_ARGS;
37521 bqConfig = ma_bpf2__get_biquad_config(pConfig);
37522 result = ma_biquad_reinit(&bqConfig, &pBPF->bq);
37523 if (result != MA_SUCCESS) {
37530 static MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
37532 ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn);
37535 static MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn)
37537 ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn);
37540 MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
37542 if (pBPF == NULL) {
37543 return MA_INVALID_ARGS;
37546 return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount);
37549 MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF)
37551 if (pBPF == NULL) {
37555 return ma_biquad_get_latency(&pBPF->bq);
37559 MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
37561 ma_bpf_config config;
37563 MA_ZERO_OBJECT(&config);
37564 config.format = format;
37565 config.channels = channels;
37566 config.sampleRate = sampleRate;
37567 config.cutoffFrequency = cutoffFrequency;
37568 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
37573 static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, ma_bpf* pBPF, ma_bool32 isNew)
37576 ma_uint32 bpf2Count;
37579 if (pBPF == NULL || pConfig == NULL) {
37580 return MA_INVALID_ARGS;
37583 /* Only supporting f32 and s16. */
37584 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
37585 return MA_INVALID_ARGS;
37588 /* The format cannot be changed after initialization. */
37589 if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) {
37590 return MA_INVALID_OPERATION;
37593 /* The channel count cannot be changed after initialization. */
37594 if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) {
37595 return MA_INVALID_OPERATION;
37598 if (pConfig->order > MA_MAX_FILTER_ORDER) {
37599 return MA_INVALID_ARGS;
37602 /* We must have an even number of order. */
37603 if ((pConfig->order & 0x1) != 0) {
37604 return MA_INVALID_ARGS;
37607 bpf2Count = pConfig->order / 2;
37609 MA_ASSERT(bpf2Count <= ma_countof(pBPF->bpf2));
37611 /* The filter order can't change between reinits. */
37613 if (pBPF->bpf2Count != bpf2Count) {
37614 return MA_INVALID_OPERATION;
37618 for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) {
37619 ma_bpf2_config bpf2Config;
37622 /* TODO: Calculate Q to make this a proper Butterworth filter. */
37625 bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
37628 result = ma_bpf2_init(&bpf2Config, &pBPF->bpf2[ibpf2]);
37630 result = ma_bpf2_reinit(&bpf2Config, &pBPF->bpf2[ibpf2]);
37633 if (result != MA_SUCCESS) {
37638 pBPF->bpf2Count = bpf2Count;
37639 pBPF->format = pConfig->format;
37640 pBPF->channels = pConfig->channels;
37645 MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, ma_bpf* pBPF)
37647 if (pBPF == NULL) {
37648 return MA_INVALID_ARGS;
37651 MA_ZERO_OBJECT(pBPF);
37653 if (pConfig == NULL) {
37654 return MA_INVALID_ARGS;
37657 return ma_bpf_reinit__internal(pConfig, pBPF, /*isNew*/MA_TRUE);
37660 MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF)
37662 return ma_bpf_reinit__internal(pConfig, pBPF, /*isNew*/MA_FALSE);
37665 MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
37670 if (pBPF == NULL) {
37671 return MA_INVALID_ARGS;
37674 /* Faster path for in-place. */
37675 if (pFramesOut == pFramesIn) {
37676 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
37677 result = ma_bpf2_process_pcm_frames(&pBPF->bpf2[ibpf2], pFramesOut, pFramesOut, frameCount);
37678 if (result != MA_SUCCESS) {
37684 /* Slightly slower path for copying. */
37685 if (pFramesOut != pFramesIn) {
37688 /* */ if (pBPF->format == ma_format_f32) {
37689 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
37690 const float* pFramesInF32 = (const float*)pFramesIn;
37692 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
37693 MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));
37695 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
37696 ma_bpf2_process_pcm_frame_f32(&pBPF->bpf2[ibpf2], pFramesOutF32, pFramesOutF32);
37699 pFramesOutF32 += pBPF->channels;
37700 pFramesInF32 += pBPF->channels;
37702 } else if (pBPF->format == ma_format_s16) {
37703 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
37704 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
37706 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
37707 MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));
37709 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
37710 ma_bpf2_process_pcm_frame_s16(&pBPF->bpf2[ibpf2], pFramesOutS16, pFramesOutS16);
37713 pFramesOutS16 += pBPF->channels;
37714 pFramesInS16 += pBPF->channels;
37717 MA_ASSERT(MA_FALSE);
37718 return MA_INVALID_OPERATION; /* Should never hit this. */
37725 MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF)
37727 if (pBPF == NULL) {
37731 return pBPF->bpf2Count*2;
37735 /**************************************************************************************************************************************************************
37739 **************************************************************************************************************************************************************/
37740 MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
37742 ma_notch2_config config;
37744 MA_ZERO_OBJECT(&config);
37745 config.format = format;
37746 config.channels = channels;
37747 config.sampleRate = sampleRate;
37749 config.frequency = frequency;
37751 if (config.q == 0) {
37752 config.q = 0.707107;
37759 static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig)
37761 ma_biquad_config bqConfig;
37768 MA_ASSERT(pConfig != NULL);
37771 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
37777 bqConfig.b1 = -2 * c;
37779 bqConfig.a0 = 1 + a;
37780 bqConfig.a1 = -2 * c;
37781 bqConfig.a2 = 1 - a;
37783 bqConfig.format = pConfig->format;
37784 bqConfig.channels = pConfig->channels;
37789 MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, ma_notch2* pFilter)
37792 ma_biquad_config bqConfig;
37794 if (pFilter == NULL) {
37795 return MA_INVALID_ARGS;
37798 MA_ZERO_OBJECT(pFilter);
37800 if (pConfig == NULL) {
37801 return MA_INVALID_ARGS;
37804 bqConfig = ma_notch2__get_biquad_config(pConfig);
37805 result = ma_biquad_init(&bqConfig, &pFilter->bq);
37806 if (result != MA_SUCCESS) {
37813 MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter)
37816 ma_biquad_config bqConfig;
37818 if (pFilter == NULL || pConfig == NULL) {
37819 return MA_INVALID_ARGS;
37822 bqConfig = ma_notch2__get_biquad_config(pConfig);
37823 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
37824 if (result != MA_SUCCESS) {
37831 static MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
37833 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
37836 static MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn)
37838 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
37841 MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
37843 if (pFilter == NULL) {
37844 return MA_INVALID_ARGS;
37847 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
37850 MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter)
37852 if (pFilter == NULL) {
37856 return ma_biquad_get_latency(&pFilter->bq);
37861 /**************************************************************************************************************************************************************
37865 **************************************************************************************************************************************************************/
37866 MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
37868 ma_peak2_config config;
37870 MA_ZERO_OBJECT(&config);
37871 config.format = format;
37872 config.channels = channels;
37873 config.sampleRate = sampleRate;
37874 config.gainDB = gainDB;
37876 config.frequency = frequency;
37878 if (config.q == 0) {
37879 config.q = 0.707107;
37886 static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig)
37888 ma_biquad_config bqConfig;
37896 MA_ASSERT(pConfig != NULL);
37899 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
37903 A = ma_pow(10, (pConfig->gainDB / 40));
37905 bqConfig.b0 = 1 + (a * A);
37906 bqConfig.b1 = -2 * c;
37907 bqConfig.b2 = 1 - (a * A);
37908 bqConfig.a0 = 1 + (a / A);
37909 bqConfig.a1 = -2 * c;
37910 bqConfig.a2 = 1 - (a / A);
37912 bqConfig.format = pConfig->format;
37913 bqConfig.channels = pConfig->channels;
37918 MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, ma_peak2* pFilter)
37921 ma_biquad_config bqConfig;
37923 if (pFilter == NULL) {
37924 return MA_INVALID_ARGS;
37927 MA_ZERO_OBJECT(pFilter);
37929 if (pConfig == NULL) {
37930 return MA_INVALID_ARGS;
37933 bqConfig = ma_peak2__get_biquad_config(pConfig);
37934 result = ma_biquad_init(&bqConfig, &pFilter->bq);
37935 if (result != MA_SUCCESS) {
37942 MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter)
37945 ma_biquad_config bqConfig;
37947 if (pFilter == NULL || pConfig == NULL) {
37948 return MA_INVALID_ARGS;
37951 bqConfig = ma_peak2__get_biquad_config(pConfig);
37952 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
37953 if (result != MA_SUCCESS) {
37960 static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
37962 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
37965 static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn)
37967 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
37970 MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
37972 if (pFilter == NULL) {
37973 return MA_INVALID_ARGS;
37976 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
37979 MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter)
37981 if (pFilter == NULL) {
37985 return ma_biquad_get_latency(&pFilter->bq);
37989 /**************************************************************************************************************************************************************
37993 **************************************************************************************************************************************************************/
37994 MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
37996 ma_loshelf2_config config;
37998 MA_ZERO_OBJECT(&config);
37999 config.format = format;
38000 config.channels = channels;
38001 config.sampleRate = sampleRate;
38002 config.gainDB = gainDB;
38003 config.shelfSlope = shelfSlope;
38004 config.frequency = frequency;
38010 static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig)
38012 ma_biquad_config bqConfig;
38021 MA_ASSERT(pConfig != NULL);
38023 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
38026 A = ma_pow(10, (pConfig->gainDB / 40));
38027 S = pConfig->shelfSlope;
38028 a = s/2 * ma_sqrt((A + 1/A) * (1/S - 1) + 2);
38029 sqrtA = 2*ma_sqrt(A)*a;
38031 bqConfig.b0 = A * ((A + 1) - (A - 1)*c + sqrtA);
38032 bqConfig.b1 = 2 * A * ((A - 1) - (A + 1)*c);
38033 bqConfig.b2 = A * ((A + 1) - (A - 1)*c - sqrtA);
38034 bqConfig.a0 = (A + 1) + (A - 1)*c + sqrtA;
38035 bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c);
38036 bqConfig.a2 = (A + 1) + (A - 1)*c - sqrtA;
38038 bqConfig.format = pConfig->format;
38039 bqConfig.channels = pConfig->channels;
38044 MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter)
38047 ma_biquad_config bqConfig;
38049 if (pFilter == NULL) {
38050 return MA_INVALID_ARGS;
38053 MA_ZERO_OBJECT(pFilter);
38055 if (pConfig == NULL) {
38056 return MA_INVALID_ARGS;
38059 bqConfig = ma_loshelf2__get_biquad_config(pConfig);
38060 result = ma_biquad_init(&bqConfig, &pFilter->bq);
38061 if (result != MA_SUCCESS) {
38068 MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter)
38071 ma_biquad_config bqConfig;
38073 if (pFilter == NULL || pConfig == NULL) {
38074 return MA_INVALID_ARGS;
38077 bqConfig = ma_loshelf2__get_biquad_config(pConfig);
38078 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
38079 if (result != MA_SUCCESS) {
38086 static MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
38088 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
38091 static MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn)
38093 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
38096 MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
38098 if (pFilter == NULL) {
38099 return MA_INVALID_ARGS;
38102 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
38105 MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter)
38107 if (pFilter == NULL) {
38111 return ma_biquad_get_latency(&pFilter->bq);
38115 /**************************************************************************************************************************************************************
38119 **************************************************************************************************************************************************************/
38120 MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
38122 ma_hishelf2_config config;
38124 MA_ZERO_OBJECT(&config);
38125 config.format = format;
38126 config.channels = channels;
38127 config.sampleRate = sampleRate;
38128 config.gainDB = gainDB;
38129 config.shelfSlope = shelfSlope;
38130 config.frequency = frequency;
38136 static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig)
38138 ma_biquad_config bqConfig;
38147 MA_ASSERT(pConfig != NULL);
38149 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
38152 A = ma_pow(10, (pConfig->gainDB / 40));
38153 S = pConfig->shelfSlope;
38154 a = s/2 * ma_sqrt((A + 1/A) * (1/S - 1) + 2);
38155 sqrtA = 2*ma_sqrt(A)*a;
38157 bqConfig.b0 = A * ((A + 1) + (A - 1)*c + sqrtA);
38158 bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c);
38159 bqConfig.b2 = A * ((A + 1) + (A - 1)*c - sqrtA);
38160 bqConfig.a0 = (A + 1) - (A - 1)*c + sqrtA;
38161 bqConfig.a1 = 2 * ((A - 1) - (A + 1)*c);
38162 bqConfig.a2 = (A + 1) - (A - 1)*c - sqrtA;
38164 bqConfig.format = pConfig->format;
38165 bqConfig.channels = pConfig->channels;
38170 MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter)
38173 ma_biquad_config bqConfig;
38175 if (pFilter == NULL) {
38176 return MA_INVALID_ARGS;
38179 MA_ZERO_OBJECT(pFilter);
38181 if (pConfig == NULL) {
38182 return MA_INVALID_ARGS;
38185 bqConfig = ma_hishelf2__get_biquad_config(pConfig);
38186 result = ma_biquad_init(&bqConfig, &pFilter->bq);
38187 if (result != MA_SUCCESS) {
38194 MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter)
38197 ma_biquad_config bqConfig;
38199 if (pFilter == NULL || pConfig == NULL) {
38200 return MA_INVALID_ARGS;
38203 bqConfig = ma_hishelf2__get_biquad_config(pConfig);
38204 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
38205 if (result != MA_SUCCESS) {
38212 static MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
38214 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
38217 static MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn)
38219 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
38222 MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
38224 if (pFilter == NULL) {
38225 return MA_INVALID_ARGS;
38228 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
38231 MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter)
38233 if (pFilter == NULL) {
38237 return ma_biquad_get_latency(&pFilter->bq);
38242 /**************************************************************************************************************************************************************
38246 **************************************************************************************************************************************************************/
38247 MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
38249 ma_linear_resampler_config config;
38250 MA_ZERO_OBJECT(&config);
38251 config.format = format;
38252 config.channels = channels;
38253 config.sampleRateIn = sampleRateIn;
38254 config.sampleRateOut = sampleRateOut;
38255 config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
38256 config.lpfNyquistFactor = 1;
38261 static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut)
38264 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
38265 be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate.
38267 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. */
38268 ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut;
38270 pResampler->inTimeFrac =
38271 (oldRateTimeWhole * newSampleRateOut) +
38272 ((oldRateTimeFract * newSampleRateOut) / oldSampleRateOut);
38274 /* Make sure the fractional part is less than the output sample rate. */
38275 pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut;
38276 pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut;
38279 static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized)
38283 ma_uint32 lpfSampleRate;
38284 double lpfCutoffFrequency;
38285 ma_lpf_config lpfConfig;
38286 ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */
38288 if (pResampler == NULL) {
38289 return MA_INVALID_ARGS;
38292 if (sampleRateIn == 0 || sampleRateOut == 0) {
38293 return MA_INVALID_ARGS;
38296 oldSampleRateOut = pResampler->config.sampleRateOut;
38298 pResampler->config.sampleRateIn = sampleRateIn;
38299 pResampler->config.sampleRateOut = sampleRateOut;
38301 /* Simplify the sample rate. */
38302 gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut);
38303 pResampler->config.sampleRateIn /= gcf;
38304 pResampler->config.sampleRateOut /= gcf;
38306 /* Always initialize the low-pass filter, even when the order is 0. */
38307 if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) {
38308 return MA_INVALID_ARGS;
38311 lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut));
38312 lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor);
38314 lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder);
38317 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
38318 getting cleared. Instead we re-initialize the filter which will maintain any cached frames.
38320 if (isResamplerAlreadyInitialized) {
38321 result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf);
38323 result = ma_lpf_init(&lpfConfig, &pResampler->lpf);
38326 if (result != MA_SUCCESS) {
38331 pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut;
38332 pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut;
38334 /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */
38335 ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut);
38340 MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, ma_linear_resampler* pResampler)
38344 if (pResampler == NULL) {
38345 return MA_INVALID_ARGS;
38348 MA_ZERO_OBJECT(pResampler);
38350 if (pConfig == NULL) {
38351 return MA_INVALID_ARGS;
38354 if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) {
38355 return MA_INVALID_ARGS;
38358 pResampler->config = *pConfig;
38360 /* Setting the rate will set up the filter and time advances for us. */
38361 result = ma_linear_resampler_set_rate_internal(pResampler, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE);
38362 if (result != MA_SUCCESS) {
38366 pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */
38367 pResampler->inTimeFrac = 0;
38372 MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler)
38374 if (pResampler == NULL) {
38379 static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift)
38385 MA_ASSERT(a <= (1<<shift));
38387 b = x * ((1<<shift) - a);
38391 return (ma_int16)(r >> shift);
38394 static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut)
38398 const ma_uint32 channels = pResampler->config.channels;
38399 const ma_uint32 shift = 12;
38401 MA_ASSERT(pResampler != NULL);
38402 MA_ASSERT(pFrameOut != NULL);
38404 a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut;
38406 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
38407 for (c = 0; c < channels; c += 1) {
38408 ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift);
38414 static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* MA_RESTRICT pFrameOut)
38418 const ma_uint32 channels = pResampler->config.channels;
38420 MA_ASSERT(pResampler != NULL);
38421 MA_ASSERT(pFrameOut != NULL);
38423 a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut;
38425 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
38426 for (c = 0; c < channels; c += 1) {
38427 float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a);
38432 static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
38434 const ma_int16* pFramesInS16;
38435 /* */ ma_int16* pFramesOutS16;
38436 ma_uint64 frameCountIn;
38437 ma_uint64 frameCountOut;
38438 ma_uint64 framesProcessedIn;
38439 ma_uint64 framesProcessedOut;
38441 MA_ASSERT(pResampler != NULL);
38442 MA_ASSERT(pFrameCountIn != NULL);
38443 MA_ASSERT(pFrameCountOut != NULL);
38445 pFramesInS16 = (const ma_int16*)pFramesIn;
38446 pFramesOutS16 = ( ma_int16*)pFramesOut;
38447 frameCountIn = *pFrameCountIn;
38448 frameCountOut = *pFrameCountOut;
38449 framesProcessedIn = 0;
38450 framesProcessedOut = 0;
38452 while (framesProcessedOut < frameCountOut) {
38453 /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */
38454 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
38455 ma_uint32 iChannel;
38457 if (pFramesInS16 != NULL) {
38458 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
38459 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
38460 pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
38462 pFramesInS16 += pResampler->config.channels;
38464 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
38465 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
38466 pResampler->x1.s16[iChannel] = 0;
38471 ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16);
38473 framesProcessedIn += 1;
38474 pResampler->inTimeInt -= 1;
38477 if (pResampler->inTimeInt > 0) {
38478 break; /* Ran out of input data. */
38481 /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */
38482 if (pFramesOutS16 != NULL) {
38483 MA_ASSERT(pResampler->inTimeInt == 0);
38484 ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
38486 pFramesOutS16 += pResampler->config.channels;
38489 framesProcessedOut += 1;
38491 /* Advance time forward. */
38492 pResampler->inTimeInt += pResampler->inAdvanceInt;
38493 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
38494 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
38495 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
38496 pResampler->inTimeInt += 1;
38500 *pFrameCountIn = framesProcessedIn;
38501 *pFrameCountOut = framesProcessedOut;
38506 static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
38508 const ma_int16* pFramesInS16;
38509 /* */ ma_int16* pFramesOutS16;
38510 ma_uint64 frameCountIn;
38511 ma_uint64 frameCountOut;
38512 ma_uint64 framesProcessedIn;
38513 ma_uint64 framesProcessedOut;
38515 MA_ASSERT(pResampler != NULL);
38516 MA_ASSERT(pFrameCountIn != NULL);
38517 MA_ASSERT(pFrameCountOut != NULL);
38519 pFramesInS16 = (const ma_int16*)pFramesIn;
38520 pFramesOutS16 = ( ma_int16*)pFramesOut;
38521 frameCountIn = *pFrameCountIn;
38522 frameCountOut = *pFrameCountOut;
38523 framesProcessedIn = 0;
38524 framesProcessedOut = 0;
38526 while (framesProcessedOut < frameCountOut) {
38527 /* Before interpolating we need to load the buffers. */
38528 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
38529 ma_uint32 iChannel;
38531 if (pFramesInS16 != NULL) {
38532 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
38533 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
38534 pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
38536 pFramesInS16 += pResampler->config.channels;
38538 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
38539 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
38540 pResampler->x1.s16[iChannel] = 0;
38544 framesProcessedIn += 1;
38545 pResampler->inTimeInt -= 1;
38548 if (pResampler->inTimeInt > 0) {
38549 break; /* Ran out of input data. */
38552 /* Getting here means the frames have been loaded and we can generate the next output frame. */
38553 if (pFramesOutS16 != NULL) {
38554 MA_ASSERT(pResampler->inTimeInt == 0);
38555 ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
38558 ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16);
38560 pFramesOutS16 += pResampler->config.channels;
38563 framesProcessedOut += 1;
38565 /* Advance time forward. */
38566 pResampler->inTimeInt += pResampler->inAdvanceInt;
38567 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
38568 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
38569 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
38570 pResampler->inTimeInt += 1;
38574 *pFrameCountIn = framesProcessedIn;
38575 *pFrameCountOut = framesProcessedOut;
38580 static ma_result ma_linear_resampler_process_pcm_frames_s16(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
38582 MA_ASSERT(pResampler != NULL);
38584 if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
38585 return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
38587 return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
38592 static ma_result ma_linear_resampler_process_pcm_frames_f32_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
38594 const float* pFramesInF32;
38595 /* */ float* pFramesOutF32;
38596 ma_uint64 frameCountIn;
38597 ma_uint64 frameCountOut;
38598 ma_uint64 framesProcessedIn;
38599 ma_uint64 framesProcessedOut;
38601 MA_ASSERT(pResampler != NULL);
38602 MA_ASSERT(pFrameCountIn != NULL);
38603 MA_ASSERT(pFrameCountOut != NULL);
38605 pFramesInF32 = (const float*)pFramesIn;
38606 pFramesOutF32 = ( float*)pFramesOut;
38607 frameCountIn = *pFrameCountIn;
38608 frameCountOut = *pFrameCountOut;
38609 framesProcessedIn = 0;
38610 framesProcessedOut = 0;
38612 while (framesProcessedOut < frameCountOut) {
38613 /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */
38614 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
38615 ma_uint32 iChannel;
38617 if (pFramesInF32 != NULL) {
38618 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
38619 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
38620 pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
38622 pFramesInF32 += pResampler->config.channels;
38624 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
38625 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
38626 pResampler->x1.f32[iChannel] = 0;
38631 ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32);
38633 framesProcessedIn += 1;
38634 pResampler->inTimeInt -= 1;
38637 if (pResampler->inTimeInt > 0) {
38638 break; /* Ran out of input data. */
38641 /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */
38642 if (pFramesOutF32 != NULL) {
38643 MA_ASSERT(pResampler->inTimeInt == 0);
38644 ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
38646 pFramesOutF32 += pResampler->config.channels;
38649 framesProcessedOut += 1;
38651 /* Advance time forward. */
38652 pResampler->inTimeInt += pResampler->inAdvanceInt;
38653 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
38654 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
38655 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
38656 pResampler->inTimeInt += 1;
38660 *pFrameCountIn = framesProcessedIn;
38661 *pFrameCountOut = framesProcessedOut;
38666 static ma_result ma_linear_resampler_process_pcm_frames_f32_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
38668 const float* pFramesInF32;
38669 /* */ float* pFramesOutF32;
38670 ma_uint64 frameCountIn;
38671 ma_uint64 frameCountOut;
38672 ma_uint64 framesProcessedIn;
38673 ma_uint64 framesProcessedOut;
38675 MA_ASSERT(pResampler != NULL);
38676 MA_ASSERT(pFrameCountIn != NULL);
38677 MA_ASSERT(pFrameCountOut != NULL);
38679 pFramesInF32 = (const float*)pFramesIn;
38680 pFramesOutF32 = ( float*)pFramesOut;
38681 frameCountIn = *pFrameCountIn;
38682 frameCountOut = *pFrameCountOut;
38683 framesProcessedIn = 0;
38684 framesProcessedOut = 0;
38686 while (framesProcessedOut < frameCountOut) {
38687 /* Before interpolating we need to load the buffers. */
38688 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
38689 ma_uint32 iChannel;
38691 if (pFramesInF32 != NULL) {
38692 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
38693 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
38694 pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
38696 pFramesInF32 += pResampler->config.channels;
38698 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
38699 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
38700 pResampler->x1.f32[iChannel] = 0;
38704 framesProcessedIn += 1;
38705 pResampler->inTimeInt -= 1;
38708 if (pResampler->inTimeInt > 0) {
38709 break; /* Ran out of input data. */
38712 /* Getting here means the frames have been loaded and we can generate the next output frame. */
38713 if (pFramesOutF32 != NULL) {
38714 MA_ASSERT(pResampler->inTimeInt == 0);
38715 ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
38718 ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32);
38720 pFramesOutF32 += pResampler->config.channels;
38723 framesProcessedOut += 1;
38725 /* Advance time forward. */
38726 pResampler->inTimeInt += pResampler->inAdvanceInt;
38727 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
38728 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
38729 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
38730 pResampler->inTimeInt += 1;
38734 *pFrameCountIn = framesProcessedIn;
38735 *pFrameCountOut = framesProcessedOut;
38740 static ma_result ma_linear_resampler_process_pcm_frames_f32(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
38742 MA_ASSERT(pResampler != NULL);
38744 if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
38745 return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
38747 return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
38752 MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
38754 if (pResampler == NULL) {
38755 return MA_INVALID_ARGS;
38758 /* */ if (pResampler->config.format == ma_format_s16) {
38759 return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
38760 } else if (pResampler->config.format == ma_format_f32) {
38761 return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
38763 /* 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(). */
38764 MA_ASSERT(MA_FALSE);
38765 return MA_INVALID_ARGS;
38770 MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
38772 return ma_linear_resampler_set_rate_internal(pResampler, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE);
38775 MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut)
38781 n = (ma_uint32)(ratioInOut * d);
38784 return MA_INVALID_ARGS; /* Ratio too small. */
38789 return ma_linear_resampler_set_rate(pResampler, n, d);
38793 MA_API ma_uint64 ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount)
38795 ma_uint64 inputFrameCount;
38797 if (pResampler == NULL) {
38801 if (outputFrameCount == 0) {
38805 /* Any whole input frames are consumed before the first output frame is generated. */
38806 inputFrameCount = pResampler->inTimeInt;
38807 outputFrameCount -= 1;
38809 /* The rest of the output frames can be calculated in constant time. */
38810 inputFrameCount += outputFrameCount * pResampler->inAdvanceInt;
38811 inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut;
38813 return inputFrameCount;
38816 MA_API ma_uint64 ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount)
38818 ma_uint64 outputFrameCount;
38819 ma_uint64 preliminaryInputFrameCountFromFrac;
38820 ma_uint64 preliminaryInputFrameCount;
38822 if (pResampler == NULL) {
38827 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
38828 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
38829 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
38830 of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames.
38832 outputFrameCount = (inputFrameCount * pResampler->config.sampleRateOut) / pResampler->config.sampleRateIn;
38835 We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is
38836 used in the logic below to determine whether or not we need to add an extra output frame.
38838 preliminaryInputFrameCountFromFrac = (pResampler->inTimeFrac + outputFrameCount*pResampler->inAdvanceFrac) / pResampler->config.sampleRateOut;
38839 preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac;
38842 If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greather than
38843 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
38844 to actually process. Otherwise we need to add the extra output frame.
38846 if (preliminaryInputFrameCount <= inputFrameCount) {
38847 outputFrameCount += 1;
38850 return outputFrameCount;
38853 MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler)
38855 if (pResampler == NULL) {
38859 return 1 + ma_lpf_get_latency(&pResampler->lpf);
38862 MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler)
38864 if (pResampler == NULL) {
38868 return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn;
38872 #if defined(ma_speex_resampler_h)
38873 #define MA_HAS_SPEEX_RESAMPLER
38875 static ma_result ma_result_from_speex_err(int err)
38879 case RESAMPLER_ERR_SUCCESS: return MA_SUCCESS;
38880 case RESAMPLER_ERR_ALLOC_FAILED: return MA_OUT_OF_MEMORY;
38881 case RESAMPLER_ERR_BAD_STATE: return MA_ERROR;
38882 case RESAMPLER_ERR_INVALID_ARG: return MA_INVALID_ARGS;
38883 case RESAMPLER_ERR_PTR_OVERLAP: return MA_INVALID_ARGS;
38884 case RESAMPLER_ERR_OVERFLOW: return MA_ERROR;
38885 default: return MA_ERROR;
38888 #endif /* ma_speex_resampler_h */
38890 MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm)
38892 ma_resampler_config config;
38894 MA_ZERO_OBJECT(&config);
38895 config.format = format;
38896 config.channels = channels;
38897 config.sampleRateIn = sampleRateIn;
38898 config.sampleRateOut = sampleRateOut;
38899 config.algorithm = algorithm;
38902 config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
38903 config.linear.lpfNyquistFactor = 1;
38906 config.speex.quality = 3; /* Cannot leave this as 0 as that is actually a valid value for Speex resampling quality. */
38911 MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pResampler)
38915 if (pResampler == NULL) {
38916 return MA_INVALID_ARGS;
38919 MA_ZERO_OBJECT(pResampler);
38921 if (pConfig == NULL) {
38922 return MA_INVALID_ARGS;
38925 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
38926 return MA_INVALID_ARGS;
38929 pResampler->config = *pConfig;
38931 switch (pConfig->algorithm)
38933 case ma_resample_algorithm_linear:
38935 ma_linear_resampler_config linearConfig;
38936 linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut);
38937 linearConfig.lpfOrder = pConfig->linear.lpfOrder;
38938 linearConfig.lpfNyquistFactor = pConfig->linear.lpfNyquistFactor;
38940 result = ma_linear_resampler_init(&linearConfig, &pResampler->state.linear);
38941 if (result != MA_SUCCESS) {
38946 case ma_resample_algorithm_speex:
38948 #if defined(MA_HAS_SPEEX_RESAMPLER)
38950 pResampler->state.speex.pSpeexResamplerState = speex_resampler_init(pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->speex.quality, &speexErr);
38951 if (pResampler->state.speex.pSpeexResamplerState == NULL) {
38952 return ma_result_from_speex_err(speexErr);
38955 /* Speex resampler not available. */
38956 return MA_NO_BACKEND;
38960 default: return MA_INVALID_ARGS;
38966 MA_API void ma_resampler_uninit(ma_resampler* pResampler)
38968 if (pResampler == NULL) {
38972 if (pResampler->config.algorithm == ma_resample_algorithm_linear) {
38973 ma_linear_resampler_uninit(&pResampler->state.linear);
38976 #if defined(MA_HAS_SPEEX_RESAMPLER)
38977 if (pResampler->config.algorithm == ma_resample_algorithm_speex) {
38978 speex_resampler_destroy((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState);
38983 static ma_result ma_resampler_process_pcm_frames__read__linear(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
38985 return ma_linear_resampler_process_pcm_frames(&pResampler->state.linear, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
38988 #if defined(MA_HAS_SPEEX_RESAMPLER)
38989 static ma_result ma_resampler_process_pcm_frames__read__speex(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
38992 ma_uint64 frameCountOut;
38993 ma_uint64 frameCountIn;
38994 ma_uint64 framesProcessedOut;
38995 ma_uint64 framesProcessedIn;
38996 unsigned int framesPerIteration = UINT_MAX;
38998 MA_ASSERT(pResampler != NULL);
38999 MA_ASSERT(pFramesOut != NULL);
39000 MA_ASSERT(pFrameCountOut != NULL);
39001 MA_ASSERT(pFrameCountIn != NULL);
39004 Reading from the Speex resampler requires a bit of dancing around for a few reasons. The first thing is that it's frame counts
39005 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
39006 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
39007 do this is for ma_resampler_get_required_input_frame_count() and ma_resampler_get_expected_output_frame_count().
39009 frameCountOut = *pFrameCountOut;
39010 frameCountIn = *pFrameCountIn;
39011 framesProcessedOut = 0;
39012 framesProcessedIn = 0;
39014 while (framesProcessedOut < frameCountOut && framesProcessedIn < frameCountIn) {
39015 unsigned int frameCountInThisIteration;
39016 unsigned int frameCountOutThisIteration;
39017 const void* pFramesInThisIteration;
39018 void* pFramesOutThisIteration;
39020 frameCountInThisIteration = framesPerIteration;
39021 if ((ma_uint64)frameCountInThisIteration > (frameCountIn - framesProcessedIn)) {
39022 frameCountInThisIteration = (unsigned int)(frameCountIn - framesProcessedIn);
39025 frameCountOutThisIteration = framesPerIteration;
39026 if ((ma_uint64)frameCountOutThisIteration > (frameCountOut - framesProcessedOut)) {
39027 frameCountOutThisIteration = (unsigned int)(frameCountOut - framesProcessedOut);
39030 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels));
39031 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels));
39033 if (pResampler->config.format == ma_format_f32) {
39034 speexErr = speex_resampler_process_interleaved_float((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, (const float*)pFramesInThisIteration, &frameCountInThisIteration, (float*)pFramesOutThisIteration, &frameCountOutThisIteration);
39035 } else if (pResampler->config.format == ma_format_s16) {
39036 speexErr = speex_resampler_process_interleaved_int((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, (const spx_int16_t*)pFramesInThisIteration, &frameCountInThisIteration, (spx_int16_t*)pFramesOutThisIteration, &frameCountOutThisIteration);
39038 /* Format not supported. Should never get here. */
39039 MA_ASSERT(MA_FALSE);
39040 return MA_INVALID_OPERATION;
39043 if (speexErr != RESAMPLER_ERR_SUCCESS) {
39044 return ma_result_from_speex_err(speexErr);
39047 framesProcessedIn += frameCountInThisIteration;
39048 framesProcessedOut += frameCountOutThisIteration;
39051 *pFrameCountOut = framesProcessedOut;
39052 *pFrameCountIn = framesProcessedIn;
39058 static ma_result ma_resampler_process_pcm_frames__read(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
39060 MA_ASSERT(pResampler != NULL);
39061 MA_ASSERT(pFramesOut != NULL);
39063 /* pFramesOut is not NULL, which means we must have a capacity. */
39064 if (pFrameCountOut == NULL) {
39065 return MA_INVALID_ARGS;
39068 /* It doesn't make sense to not have any input frames to process. */
39069 if (pFrameCountIn == NULL || pFramesIn == NULL) {
39070 return MA_INVALID_ARGS;
39073 switch (pResampler->config.algorithm)
39075 case ma_resample_algorithm_linear:
39077 return ma_resampler_process_pcm_frames__read__linear(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
39080 case ma_resample_algorithm_speex:
39082 #if defined(MA_HAS_SPEEX_RESAMPLER)
39083 return ma_resampler_process_pcm_frames__read__speex(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
39092 /* Should never get here. */
39093 MA_ASSERT(MA_FALSE);
39094 return MA_INVALID_ARGS;
39098 static ma_result ma_resampler_process_pcm_frames__seek__linear(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut)
39100 MA_ASSERT(pResampler != NULL);
39102 /* Seeking is supported natively by the linear resampler. */
39103 return ma_linear_resampler_process_pcm_frames(&pResampler->state.linear, pFramesIn, pFrameCountIn, NULL, pFrameCountOut);
39106 #if defined(MA_HAS_SPEEX_RESAMPLER)
39107 static ma_result ma_resampler_process_pcm_frames__seek__speex(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut)
39109 /* The generic seek method is implemented in on top of ma_resampler_process_pcm_frames__read() by just processing into a dummy buffer. */
39110 float devnull[4096];
39111 ma_uint64 totalOutputFramesToProcess;
39112 ma_uint64 totalOutputFramesProcessed;
39113 ma_uint64 totalInputFramesProcessed;
39117 MA_ASSERT(pResampler != NULL);
39119 totalOutputFramesProcessed = 0;
39120 totalInputFramesProcessed = 0;
39121 bpf = ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels);
39123 if (pFrameCountOut != NULL) {
39124 /* Seek by output frames. */
39125 totalOutputFramesToProcess = *pFrameCountOut;
39127 /* Seek by input frames. */
39128 MA_ASSERT(pFrameCountIn != NULL);
39129 totalOutputFramesToProcess = ma_resampler_get_expected_output_frame_count(pResampler, *pFrameCountIn);
39132 if (pFramesIn != NULL) {
39133 /* Process input data. */
39134 MA_ASSERT(pFrameCountIn != NULL);
39135 while (totalOutputFramesProcessed < totalOutputFramesToProcess && totalInputFramesProcessed < *pFrameCountIn) {
39136 ma_uint64 inputFramesToProcessThisIteration = (*pFrameCountIn - totalInputFramesProcessed);
39137 ma_uint64 outputFramesToProcessThisIteration = (totalOutputFramesToProcess - totalOutputFramesProcessed);
39138 if (outputFramesToProcessThisIteration > sizeof(devnull) / bpf) {
39139 outputFramesToProcessThisIteration = sizeof(devnull) / bpf;
39142 result = ma_resampler_process_pcm_frames__read(pResampler, ma_offset_ptr(pFramesIn, totalInputFramesProcessed*bpf), &inputFramesToProcessThisIteration, ma_offset_ptr(devnull, totalOutputFramesProcessed*bpf), &outputFramesToProcessThisIteration);
39143 if (result != MA_SUCCESS) {
39147 totalOutputFramesProcessed += outputFramesToProcessThisIteration;
39148 totalInputFramesProcessed += inputFramesToProcessThisIteration;
39151 /* Don't process input data - just update timing and filter state as if zeroes were passed in. */
39152 while (totalOutputFramesProcessed < totalOutputFramesToProcess) {
39153 ma_uint64 inputFramesToProcessThisIteration = 16384;
39154 ma_uint64 outputFramesToProcessThisIteration = (totalOutputFramesToProcess - totalOutputFramesProcessed);
39155 if (outputFramesToProcessThisIteration > sizeof(devnull) / bpf) {
39156 outputFramesToProcessThisIteration = sizeof(devnull) / bpf;
39159 result = ma_resampler_process_pcm_frames__read(pResampler, NULL, &inputFramesToProcessThisIteration, ma_offset_ptr(devnull, totalOutputFramesProcessed*bpf), &outputFramesToProcessThisIteration);
39160 if (result != MA_SUCCESS) {
39164 totalOutputFramesProcessed += outputFramesToProcessThisIteration;
39165 totalInputFramesProcessed += inputFramesToProcessThisIteration;
39170 if (pFrameCountIn != NULL) {
39171 *pFrameCountIn = totalInputFramesProcessed;
39173 if (pFrameCountOut != NULL) {
39174 *pFrameCountOut = totalOutputFramesProcessed;
39181 static ma_result ma_resampler_process_pcm_frames__seek(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut)
39183 MA_ASSERT(pResampler != NULL);
39185 switch (pResampler->config.algorithm)
39187 case ma_resample_algorithm_linear:
39189 return ma_resampler_process_pcm_frames__seek__linear(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut);
39192 case ma_resample_algorithm_speex:
39194 #if defined(MA_HAS_SPEEX_RESAMPLER)
39195 return ma_resampler_process_pcm_frames__seek__speex(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut);
39204 /* Should never hit this. */
39205 MA_ASSERT(MA_FALSE);
39206 return MA_INVALID_ARGS;
39210 MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
39212 if (pResampler == NULL) {
39213 return MA_INVALID_ARGS;
39216 if (pFrameCountOut == NULL && pFrameCountIn == NULL) {
39217 return MA_INVALID_ARGS;
39220 if (pFramesOut != NULL) {
39222 return ma_resampler_process_pcm_frames__read(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
39225 return ma_resampler_process_pcm_frames__seek(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut);
39229 MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
39231 if (pResampler == NULL) {
39232 return MA_INVALID_ARGS;
39235 if (sampleRateIn == 0 || sampleRateOut == 0) {
39236 return MA_INVALID_ARGS;
39239 pResampler->config.sampleRateIn = sampleRateIn;
39240 pResampler->config.sampleRateOut = sampleRateOut;
39242 switch (pResampler->config.algorithm)
39244 case ma_resample_algorithm_linear:
39246 return ma_linear_resampler_set_rate(&pResampler->state.linear, sampleRateIn, sampleRateOut);
39249 case ma_resample_algorithm_speex:
39251 #if defined(MA_HAS_SPEEX_RESAMPLER)
39252 return ma_result_from_speex_err(speex_resampler_set_rate((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, sampleRateIn, sampleRateOut));
39261 /* Should never get here. */
39262 MA_ASSERT(MA_FALSE);
39263 return MA_INVALID_OPERATION;
39266 MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio)
39268 if (pResampler == NULL) {
39269 return MA_INVALID_ARGS;
39272 if (pResampler->config.algorithm == ma_resample_algorithm_linear) {
39273 return ma_linear_resampler_set_rate_ratio(&pResampler->state.linear, ratio);
39275 /* Getting here means the backend does not have native support for setting the rate as a ratio so we just do it generically. */
39280 n = (ma_uint32)(ratio * d);
39283 return MA_INVALID_ARGS; /* Ratio too small. */
39288 return ma_resampler_set_rate(pResampler, n, d);
39292 MA_API ma_uint64 ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount)
39294 if (pResampler == NULL) {
39298 if (outputFrameCount == 0) {
39302 switch (pResampler->config.algorithm)
39304 case ma_resample_algorithm_linear:
39306 return ma_linear_resampler_get_required_input_frame_count(&pResampler->state.linear, outputFrameCount);
39309 case ma_resample_algorithm_speex:
39311 #if defined(MA_HAS_SPEEX_RESAMPLER)
39312 spx_uint64_t count;
39313 int speexErr = ma_speex_resampler_get_required_input_frame_count((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, outputFrameCount, &count);
39314 if (speexErr != RESAMPLER_ERR_SUCCESS) {
39318 return (ma_uint64)count;
39327 /* Should never get here. */
39328 MA_ASSERT(MA_FALSE);
39332 MA_API ma_uint64 ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount)
39334 if (pResampler == NULL) {
39335 return 0; /* Invalid args. */
39338 if (inputFrameCount == 0) {
39342 switch (pResampler->config.algorithm)
39344 case ma_resample_algorithm_linear:
39346 return ma_linear_resampler_get_expected_output_frame_count(&pResampler->state.linear, inputFrameCount);
39349 case ma_resample_algorithm_speex:
39351 #if defined(MA_HAS_SPEEX_RESAMPLER)
39352 spx_uint64_t count;
39353 int speexErr = ma_speex_resampler_get_expected_output_frame_count((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, inputFrameCount, &count);
39354 if (speexErr != RESAMPLER_ERR_SUCCESS) {
39358 return (ma_uint64)count;
39367 /* Should never get here. */
39368 MA_ASSERT(MA_FALSE);
39372 MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler)
39374 if (pResampler == NULL) {
39378 switch (pResampler->config.algorithm)
39380 case ma_resample_algorithm_linear:
39382 return ma_linear_resampler_get_input_latency(&pResampler->state.linear);
39385 case ma_resample_algorithm_speex:
39387 #if defined(MA_HAS_SPEEX_RESAMPLER)
39388 return (ma_uint64)ma_speex_resampler_get_input_latency((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState);
39397 /* Should never get here. */
39398 MA_ASSERT(MA_FALSE);
39402 MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler)
39404 if (pResampler == NULL) {
39408 switch (pResampler->config.algorithm)
39410 case ma_resample_algorithm_linear:
39412 return ma_linear_resampler_get_output_latency(&pResampler->state.linear);
39415 case ma_resample_algorithm_speex:
39417 #if defined(MA_HAS_SPEEX_RESAMPLER)
39418 return (ma_uint64)ma_speex_resampler_get_output_latency((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState);
39427 /* Should never get here. */
39428 MA_ASSERT(MA_FALSE);
39432 /**************************************************************************************************************************************************************
39436 **************************************************************************************************************************************************************/
39437 #ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT
39438 #define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12
39441 #define MA_PLANE_LEFT 0
39442 #define MA_PLANE_RIGHT 1
39443 #define MA_PLANE_FRONT 2
39444 #define MA_PLANE_BACK 3
39445 #define MA_PLANE_BOTTOM 4
39446 #define MA_PLANE_TOP 5
39448 static float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = {
39449 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */
39450 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */
39451 { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */
39452 { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */
39453 { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */
39454 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */
39455 { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */
39456 { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */
39457 { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */
39458 { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */
39459 { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */
39460 { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */
39461 { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */
39462 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */
39463 { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */
39464 { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */
39465 { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */
39466 { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */
39467 { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */
39468 { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */
39469 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */
39470 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */
39471 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */
39472 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */
39473 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */
39474 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */
39475 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */
39476 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */
39477 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */
39478 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */
39479 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */
39480 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */
39481 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */
39482 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */
39483 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */
39484 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */
39485 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */
39486 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */
39487 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */
39488 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */
39489 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */
39490 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */
39491 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */
39492 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */
39493 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */
39494 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */
39495 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */
39496 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */
39497 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */
39498 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */
39499 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */
39500 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */
39503 static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB)
39506 Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to
39507 the following output configuration:
39513 The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount
39514 of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated.
39516 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
39517 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
39518 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
39519 receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between
39520 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
39521 across 3 spatial dimensions.
39523 The first thing to do is figure out how each speaker's volume is spread over each of plane:
39524 - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane
39525 - side/left: 1 plane (left only) = 1/1 = entire volume from left plane
39526 - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane
39527 - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane
39529 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
39530 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
39531 taken by the other to produce the final contribution.
39534 /* Contribution = Sum(Volume to Give * Volume to Take) */
39535 float contribution =
39536 g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] +
39537 g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] +
39538 g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] +
39539 g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] +
39540 g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] +
39541 g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5];
39543 return contribution;
39546 MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode)
39548 ma_channel_converter_config config;
39550 /* Channel counts need to be clamped. */
39551 channelsIn = ma_min(channelsIn, ma_countof(config.channelMapIn));
39552 channelsOut = ma_min(channelsOut, ma_countof(config.channelMapOut));
39554 MA_ZERO_OBJECT(&config);
39555 config.format = format;
39556 config.channelsIn = channelsIn;
39557 config.channelsOut = channelsOut;
39558 ma_channel_map_copy_or_default(config.channelMapIn, pChannelMapIn, channelsIn);
39559 ma_channel_map_copy_or_default(config.channelMapOut, pChannelMapOut, channelsOut);
39560 config.mixingMode = mixingMode;
39565 static ma_int32 ma_channel_converter_float_to_fixed(float x)
39567 return (ma_int32)(x * (1<<MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT));
39570 static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition)
39574 if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) {
39578 for (i = 0; i < 6; ++i) { /* Each side of a cube. */
39579 if (g_maChannelPlaneRatios[channelPosition][i] != 0) {
39587 MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, ma_channel_converter* pConverter)
39589 ma_uint32 iChannelIn;
39590 ma_uint32 iChannelOut;
39592 if (pConverter == NULL) {
39593 return MA_INVALID_ARGS;
39596 MA_ZERO_OBJECT(pConverter);
39598 if (pConfig == NULL) {
39599 return MA_INVALID_ARGS;
39602 /* Basic validation for channel counts. */
39603 if (pConfig->channelsIn < MA_MIN_CHANNELS || pConfig->channelsIn > MA_MAX_CHANNELS ||
39604 pConfig->channelsOut < MA_MIN_CHANNELS || pConfig->channelsOut > MA_MAX_CHANNELS) {
39605 return MA_INVALID_ARGS;
39608 if (!ma_channel_map_valid(pConfig->channelsIn, pConfig->channelMapIn)) {
39609 return MA_INVALID_ARGS; /* Invalid input channel map. */
39611 if (!ma_channel_map_valid(pConfig->channelsOut, pConfig->channelMapOut)) {
39612 return MA_INVALID_ARGS; /* Invalid output channel map. */
39615 pConverter->format = pConfig->format;
39616 pConverter->channelsIn = pConfig->channelsIn;
39617 pConverter->channelsOut = pConfig->channelsOut;
39618 ma_channel_map_copy(pConverter->channelMapIn, pConfig->channelMapIn, pConfig->channelsIn);
39619 ma_channel_map_copy(pConverter->channelMapOut, pConfig->channelMapOut, pConfig->channelsOut);
39620 pConverter->mixingMode = pConfig->mixingMode;
39622 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
39623 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
39624 if (pConverter->format == ma_format_f32) {
39625 pConverter->weights.f32[iChannelIn][iChannelOut] = pConfig->weights[iChannelIn][iChannelOut];
39627 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(pConfig->weights[iChannelIn][iChannelOut]);
39634 /* If the input and output channels and channel maps are the same we should use a passthrough. */
39635 if (pConverter->channelsIn == pConverter->channelsOut) {
39636 if (ma_channel_map_equal(pConverter->channelsIn, pConverter->channelMapIn, pConverter->channelMapOut)) {
39637 pConverter->isPassthrough = MA_TRUE;
39639 if (ma_channel_map_blank(pConverter->channelsIn, pConverter->channelMapIn) || ma_channel_map_blank(pConverter->channelsOut, pConverter->channelMapOut)) {
39640 pConverter->isPassthrough = MA_TRUE;
39646 We can use a simple case for expanding the mono channel. This will used when expanding a mono input into any output so long
39647 as no LFE is present in the output.
39649 if (!pConverter->isPassthrough) {
39650 if (pConverter->channelsIn == 1 && pConverter->channelMapIn[0] == MA_CHANNEL_MONO) {
39651 /* Optimal case if no LFE is in the output channel map. */
39652 pConverter->isSimpleMonoExpansion = MA_TRUE;
39653 if (ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->channelMapOut, MA_CHANNEL_LFE)) {
39654 pConverter->isSimpleMonoExpansion = MA_FALSE;
39659 /* Another optimized case is stereo to mono. */
39660 if (!pConverter->isPassthrough) {
39661 if (pConverter->channelsOut == 1 && pConverter->channelMapOut[0] == MA_CHANNEL_MONO && pConverter->channelsIn == 2) {
39662 /* Optimal case if no LFE is in the input channel map. */
39663 pConverter->isStereoToMono = MA_TRUE;
39664 if (ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->channelMapIn, MA_CHANNEL_LFE)) {
39665 pConverter->isStereoToMono = MA_FALSE;
39672 Here is where we do a bit of pre-processing to know how each channel should be combined to make up the output. Rules:
39674 1) If it's a passthrough, do nothing - it's just a simple memcpy().
39675 2) If the channel counts are the same and every channel position in the input map is present in the output map, use a
39676 simple shuffle. An example might be different 5.1 channel layouts.
39677 3) Otherwise channels are blended based on spatial locality.
39679 if (!pConverter->isPassthrough) {
39680 if (pConverter->channelsIn == pConverter->channelsOut) {
39681 ma_bool32 areAllChannelPositionsPresent = MA_TRUE;
39682 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
39683 ma_bool32 isInputChannelPositionInOutput = MA_FALSE;
39684 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
39685 if (pConverter->channelMapIn[iChannelIn] == pConverter->channelMapOut[iChannelOut]) {
39686 isInputChannelPositionInOutput = MA_TRUE;
39691 if (!isInputChannelPositionInOutput) {
39692 areAllChannelPositionsPresent = MA_FALSE;
39697 if (areAllChannelPositionsPresent) {
39698 pConverter->isSimpleShuffle = MA_TRUE;
39701 All the router will be doing is rearranging channels which means all we need to do is use a shuffling table which is just
39702 a mapping between the index of the input channel to the index of the output channel.
39704 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
39705 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
39706 if (pConverter->channelMapIn[iChannelIn] == pConverter->channelMapOut[iChannelOut]) {
39707 pConverter->shuffleTable[iChannelIn] = (ma_uint8)iChannelOut;
39718 Here is where weights are calculated. Note that we calculate the weights at all times, even when using a passthrough and simple
39719 shuffling. We use different algorithms for calculating weights depending on our mixing mode.
39721 In simple mode we don't do any blending (except for converting between mono, which is done in a later step). Instead we just
39722 map 1:1 matching channels. In this mode, if no channels in the input channel map correspond to anything in the output channel
39723 map, nothing will be heard!
39726 /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */
39727 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
39728 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
39730 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
39731 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
39733 if (channelPosIn == channelPosOut) {
39734 if (pConverter->format == ma_format_f32) {
39735 pConverter->weights.f32[iChannelIn][iChannelOut] = 1;
39737 pConverter->weights.s16[iChannelIn][iChannelOut] = (1 << MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT);
39744 The mono channel is accumulated on all other channels, except LFE. Make sure in this loop we exclude output mono channels since
39745 they were handled in the pass above.
39747 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
39748 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
39750 if (channelPosIn == MA_CHANNEL_MONO) {
39751 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
39752 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
39754 if (channelPosOut != MA_CHANNEL_NONE && channelPosOut != MA_CHANNEL_MONO && channelPosOut != MA_CHANNEL_LFE) {
39755 if (pConverter->format == ma_format_f32) {
39756 pConverter->weights.f32[iChannelIn][iChannelOut] = 1;
39758 pConverter->weights.s16[iChannelIn][iChannelOut] = (1 << MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT);
39765 /* The output mono channel is the average of all non-none, non-mono and non-lfe input channels. */
39768 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
39769 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
39771 if (channelPosIn != MA_CHANNEL_NONE && channelPosIn != MA_CHANNEL_MONO && channelPosIn != MA_CHANNEL_LFE) {
39777 float monoWeight = 1.0f / len;
39779 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
39780 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
39782 if (channelPosOut == MA_CHANNEL_MONO) {
39783 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
39784 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
39786 if (channelPosIn != MA_CHANNEL_NONE && channelPosIn != MA_CHANNEL_MONO && channelPosIn != MA_CHANNEL_LFE) {
39787 if (pConverter->format == ma_format_f32) {
39788 pConverter->weights.f32[iChannelIn][iChannelOut] = monoWeight;
39790 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(monoWeight);
39800 /* Input and output channels that are not present on the other side need to be blended in based on spatial locality. */
39801 switch (pConverter->mixingMode)
39803 case ma_channel_mix_mode_rectangular:
39805 /* Unmapped input channels. */
39806 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
39807 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
39809 if (ma_is_spatial_channel_position(channelPosIn)) {
39810 if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->channelMapOut, channelPosIn)) {
39811 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
39812 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
39814 if (ma_is_spatial_channel_position(channelPosOut)) {
39816 if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
39817 weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
39820 /* Only apply the weight if we haven't already got some contribution from the respective channels. */
39821 if (pConverter->format == ma_format_f32) {
39822 if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
39823 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
39826 if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
39827 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
39836 /* Unmapped output channels. */
39837 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
39838 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
39840 if (ma_is_spatial_channel_position(channelPosOut)) {
39841 if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->channelMapIn, channelPosOut)) {
39842 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
39843 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
39845 if (ma_is_spatial_channel_position(channelPosIn)) {
39847 if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
39848 weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
39851 /* Only apply the weight if we haven't already got some contribution from the respective channels. */
39852 if (pConverter->format == ma_format_f32) {
39853 if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
39854 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
39857 if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
39858 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
39868 case ma_channel_mix_mode_simple:
39870 /* In simple mode, excess channels need to be silenced or dropped. */
39871 ma_uint32 iChannel;
39872 for (iChannel = 0; iChannel < ma_min(pConverter->channelsIn, pConverter->channelsOut); iChannel += 1) {
39873 if (pConverter->format == ma_format_f32) {
39874 if (pConverter->weights.f32[iChannel][iChannel] == 0) {
39875 pConverter->weights.f32[iChannel][iChannel] = 1;
39878 if (pConverter->weights.s16[iChannel][iChannel] == 0) {
39879 pConverter->weights.s16[iChannel][iChannel] = ma_channel_converter_float_to_fixed(1);
39885 case ma_channel_mix_mode_custom_weights:
39896 MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter)
39898 if (pConverter == NULL) {
39903 static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
39905 MA_ASSERT(pConverter != NULL);
39906 MA_ASSERT(pFramesOut != NULL);
39907 MA_ASSERT(pFramesIn != NULL);
39909 ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
39913 static ma_result ma_channel_converter_process_pcm_frames__simple_shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
39916 ma_uint32 iChannelIn;
39918 MA_ASSERT(pConverter != NULL);
39919 MA_ASSERT(pFramesOut != NULL);
39920 MA_ASSERT(pFramesIn != NULL);
39921 MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut);
39923 switch (pConverter->format)
39927 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
39928 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
39930 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
39931 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
39932 pFramesOutU8[pConverter->shuffleTable[iChannelIn]] = pFramesInU8[iChannelIn];
39935 pFramesOutU8 += pConverter->channelsOut;
39936 pFramesInU8 += pConverter->channelsIn;
39940 case ma_format_s16:
39942 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
39943 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
39945 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
39946 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
39947 pFramesOutS16[pConverter->shuffleTable[iChannelIn]] = pFramesInS16[iChannelIn];
39950 pFramesOutS16 += pConverter->channelsOut;
39951 pFramesInS16 += pConverter->channelsIn;
39955 case ma_format_s24:
39957 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
39958 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
39960 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
39961 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
39962 ma_uint32 iChannelOut = pConverter->shuffleTable[iChannelIn];
39963 pFramesOutS24[iChannelOut*3 + 0] = pFramesInS24[iChannelIn*3 + 0];
39964 pFramesOutS24[iChannelOut*3 + 1] = pFramesInS24[iChannelIn*3 + 1];
39965 pFramesOutS24[iChannelOut*3 + 2] = pFramesInS24[iChannelIn*3 + 2];
39968 pFramesOutS24 += pConverter->channelsOut*3;
39969 pFramesInS24 += pConverter->channelsIn*3;
39973 case ma_format_s32:
39975 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
39976 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
39978 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
39979 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
39980 pFramesOutS32[pConverter->shuffleTable[iChannelIn]] = pFramesInS32[iChannelIn];
39983 pFramesOutS32 += pConverter->channelsOut;
39984 pFramesInS32 += pConverter->channelsIn;
39988 case ma_format_f32:
39990 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
39991 const float* pFramesInF32 = (const float*)pFramesIn;
39993 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
39994 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
39995 pFramesOutF32[pConverter->shuffleTable[iChannelIn]] = pFramesInF32[iChannelIn];
39998 pFramesOutF32 += pConverter->channelsOut;
39999 pFramesInF32 += pConverter->channelsIn;
40003 default: return MA_INVALID_OPERATION; /* Unknown format. */
40009 static ma_result ma_channel_converter_process_pcm_frames__simple_mono_expansion(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
40013 MA_ASSERT(pConverter != NULL);
40014 MA_ASSERT(pFramesOut != NULL);
40015 MA_ASSERT(pFramesIn != NULL);
40016 MA_ASSERT(pConverter->channelsIn == 1);
40018 switch (pConverter->format)
40022 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
40023 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
40025 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40026 ma_uint32 iChannel;
40027 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
40028 pFramesOutU8[iFrame*pConverter->channelsOut + iChannel] = pFramesInU8[iFrame];
40033 case ma_format_s16:
40035 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
40036 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
40038 if (pConverter->channelsOut == 2) {
40039 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40040 pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame];
40041 pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame];
40044 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40045 ma_uint32 iChannel;
40046 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
40047 pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame];
40053 case ma_format_s24:
40055 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
40056 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
40058 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40059 ma_uint32 iChannel;
40060 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
40061 ma_uint64 iSampleOut = iFrame*pConverter->channelsOut + iChannel;
40062 ma_uint64 iSampleIn = iFrame;
40063 pFramesOutS24[iSampleOut*3 + 0] = pFramesInS24[iSampleIn*3 + 0];
40064 pFramesOutS24[iSampleOut*3 + 1] = pFramesInS24[iSampleIn*3 + 1];
40065 pFramesOutS24[iSampleOut*3 + 2] = pFramesInS24[iSampleIn*3 + 2];
40070 case ma_format_s32:
40072 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
40073 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
40075 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40076 ma_uint32 iChannel;
40077 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
40078 pFramesOutS32[iFrame*pConverter->channelsOut + iChannel] = pFramesInS32[iFrame];
40083 case ma_format_f32:
40085 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
40086 const float* pFramesInF32 = (const float*)pFramesIn;
40088 if (pConverter->channelsOut == 2) {
40089 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40090 pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame];
40091 pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame];
40094 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40095 ma_uint32 iChannel;
40096 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
40097 pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame];
40103 default: return MA_INVALID_OPERATION; /* Unknown format. */
40109 static ma_result ma_channel_converter_process_pcm_frames__stereo_to_mono(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
40113 MA_ASSERT(pConverter != NULL);
40114 MA_ASSERT(pFramesOut != NULL);
40115 MA_ASSERT(pFramesIn != NULL);
40116 MA_ASSERT(pConverter->channelsIn == 2);
40117 MA_ASSERT(pConverter->channelsOut == 1);
40119 switch (pConverter->format)
40123 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
40124 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
40126 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40127 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));
40131 case ma_format_s16:
40133 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
40134 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
40136 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40137 pFramesOutS16[iFrame] = (ma_int16)(((ma_int32)pFramesInS16[iFrame*2+0] + (ma_int32)pFramesInS16[iFrame*2+1]) / 2);
40141 case ma_format_s24:
40143 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
40144 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
40146 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40147 ma_int64 s24_0 = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*2+0)*3]);
40148 ma_int64 s24_1 = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*2+1)*3]);
40149 ma_pcm_sample_s32_to_s24_no_scale((s24_0 + s24_1) / 2, &pFramesOutS24[iFrame*3]);
40153 case ma_format_s32:
40155 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
40156 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
40158 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40159 pFramesOutS32[iFrame] = (ma_int16)(((ma_int32)pFramesInS32[iFrame*2+0] + (ma_int32)pFramesInS32[iFrame*2+1]) / 2);
40163 case ma_format_f32:
40165 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
40166 const float* pFramesInF32 = (const float*)pFramesIn;
40168 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40169 pFramesOutF32[iFrame] = (pFramesInF32[iFrame*2+0] + pFramesInF32[iFrame*2+0]) * 0.5f;
40173 default: return MA_INVALID_OPERATION; /* Unknown format. */
40179 static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
40182 ma_uint32 iChannelIn;
40183 ma_uint32 iChannelOut;
40185 MA_ASSERT(pConverter != NULL);
40186 MA_ASSERT(pFramesOut != NULL);
40187 MA_ASSERT(pFramesIn != NULL);
40189 /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */
40192 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
40195 switch (pConverter->format)
40199 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
40200 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
40202 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
40203 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40204 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40205 ma_int16 u8_O = ma_pcm_sample_u8_to_s16_no_scale(pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut]);
40206 ma_int16 u8_I = ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8 [iFrame*pConverter->channelsIn + iChannelIn ]);
40207 ma_int32 s = (ma_int32)ma_clamp(u8_O + ((u8_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -128, 127);
40208 pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_u8((ma_int16)s);
40214 case ma_format_s16:
40216 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
40217 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
40219 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
40220 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40221 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40222 ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut];
40223 s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
40225 pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767);
40231 case ma_format_s24:
40233 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
40234 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
40236 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
40237 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40238 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40239 ma_int64 s24_O = ma_pcm_sample_s24_to_s32_no_scale(&pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]);
40240 ma_int64 s24_I = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24 [(iFrame*pConverter->channelsIn + iChannelIn )*3]);
40241 ma_int64 s24 = (ma_int32)ma_clamp(s24_O + ((s24_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -8388608, 8388607);
40242 ma_pcm_sample_s32_to_s24_no_scale(s24, &pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]);
40248 case ma_format_s32:
40250 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
40251 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
40253 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
40254 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40255 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40256 ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut];
40257 s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
40259 pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s);
40265 case ma_format_f32:
40267 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
40268 const float* pFramesInF32 = (const float*)pFramesIn;
40270 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
40271 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40272 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40273 pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut];
40279 default: return MA_INVALID_OPERATION; /* Unknown format. */
40285 MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
40287 if (pConverter == NULL) {
40288 return MA_INVALID_ARGS;
40291 if (pFramesOut == NULL) {
40292 return MA_INVALID_ARGS;
40295 if (pFramesIn == NULL) {
40296 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
40300 if (pConverter->isPassthrough) {
40301 return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount);
40302 } else if (pConverter->isSimpleShuffle) {
40303 return ma_channel_converter_process_pcm_frames__simple_shuffle(pConverter, pFramesOut, pFramesIn, frameCount);
40304 } else if (pConverter->isSimpleMonoExpansion) {
40305 return ma_channel_converter_process_pcm_frames__simple_mono_expansion(pConverter, pFramesOut, pFramesIn, frameCount);
40306 } else if (pConverter->isStereoToMono) {
40307 return ma_channel_converter_process_pcm_frames__stereo_to_mono(pConverter, pFramesOut, pFramesIn, frameCount);
40309 return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount);
40314 /**************************************************************************************************************************************************************
40318 **************************************************************************************************************************************************************/
40319 MA_API ma_data_converter_config ma_data_converter_config_init_default()
40321 ma_data_converter_config config;
40322 MA_ZERO_OBJECT(&config);
40324 config.ditherMode = ma_dither_mode_none;
40325 config.resampling.algorithm = ma_resample_algorithm_linear;
40326 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. */
40328 /* Linear resampling defaults. */
40329 config.resampling.linear.lpfOrder = 1;
40330 config.resampling.linear.lpfNyquistFactor = 1;
40332 /* Speex resampling defaults. */
40333 config.resampling.speex.quality = 3;
40338 MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
40340 ma_data_converter_config config = ma_data_converter_config_init_default();
40341 config.formatIn = formatIn;
40342 config.formatOut = formatOut;
40343 config.channelsIn = ma_min(channelsIn, MA_MAX_CHANNELS);
40344 config.channelsOut = ma_min(channelsOut, MA_MAX_CHANNELS);
40345 config.sampleRateIn = sampleRateIn;
40346 config.sampleRateOut = sampleRateOut;
40351 MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, ma_data_converter* pConverter)
40354 ma_format midFormat;
40356 if (pConverter == NULL) {
40357 return MA_INVALID_ARGS;
40360 MA_ZERO_OBJECT(pConverter);
40362 if (pConfig == NULL) {
40363 return MA_INVALID_ARGS;
40366 pConverter->config = *pConfig;
40368 /* Basic validation. */
40369 if (pConfig->channelsIn < MA_MIN_CHANNELS || pConfig->channelsOut < MA_MIN_CHANNELS ||
40370 pConfig->channelsIn > MA_MAX_CHANNELS || pConfig->channelsOut > MA_MAX_CHANNELS) {
40371 return MA_INVALID_ARGS;
40375 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
40376 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
40377 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
40380 /* */ if (pConverter->config.formatOut == ma_format_s16 || pConverter->config.formatOut == ma_format_f32) {
40381 midFormat = pConverter->config.formatOut;
40382 } else if (pConverter->config.formatIn == ma_format_s16 || pConverter->config.formatIn == ma_format_f32) {
40383 midFormat = pConverter->config.formatIn;
40385 midFormat = ma_format_f32;
40388 /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */
40390 ma_uint32 iChannelIn;
40391 ma_uint32 iChannelOut;
40392 ma_channel_converter_config channelConverterConfig;
40394 channelConverterConfig = ma_channel_converter_config_init(midFormat, pConverter->config.channelsIn, pConverter->config.channelMapIn, pConverter->config.channelsOut, pConverter->config.channelMapOut, pConverter->config.channelMixMode);
40396 /* Channel weights. */
40397 for (iChannelIn = 0; iChannelIn < pConverter->config.channelsIn; iChannelIn += 1) {
40398 for (iChannelOut = 0; iChannelOut < pConverter->config.channelsOut; iChannelOut += 1) {
40399 channelConverterConfig.weights[iChannelIn][iChannelOut] = pConverter->config.channelWeights[iChannelIn][iChannelOut];
40403 result = ma_channel_converter_init(&channelConverterConfig, &pConverter->channelConverter);
40404 if (result != MA_SUCCESS) {
40408 /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */
40409 if (pConverter->channelConverter.isPassthrough == MA_FALSE) {
40410 pConverter->hasChannelConverter = MA_TRUE;
40415 /* 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. */
40416 if (pConverter->config.resampling.allowDynamicSampleRate == MA_FALSE) {
40417 pConverter->config.resampling.allowDynamicSampleRate = pConverter->config.sampleRateIn != pConverter->config.sampleRateOut;
40421 if (pConverter->config.resampling.allowDynamicSampleRate) {
40422 ma_resampler_config resamplerConfig;
40423 ma_uint32 resamplerChannels;
40425 /* 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. */
40426 if (pConverter->config.channelsIn < pConverter->config.channelsOut) {
40427 resamplerChannels = pConverter->config.channelsIn;
40429 resamplerChannels = pConverter->config.channelsOut;
40432 resamplerConfig = ma_resampler_config_init(midFormat, resamplerChannels, pConverter->config.sampleRateIn, pConverter->config.sampleRateOut, pConverter->config.resampling.algorithm);
40433 resamplerConfig.linear.lpfOrder = pConverter->config.resampling.linear.lpfOrder;
40434 resamplerConfig.linear.lpfNyquistFactor = pConverter->config.resampling.linear.lpfNyquistFactor;
40435 resamplerConfig.speex.quality = pConverter->config.resampling.speex.quality;
40437 result = ma_resampler_init(&resamplerConfig, &pConverter->resampler);
40438 if (result != MA_SUCCESS) {
40442 pConverter->hasResampler = MA_TRUE;
40446 /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */
40447 if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) {
40448 /* 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. */
40449 if (pConverter->config.formatIn == pConverter->config.formatOut) {
40450 /* The formats are the same so we can just pass through. */
40451 pConverter->hasPreFormatConversion = MA_FALSE;
40452 pConverter->hasPostFormatConversion = MA_FALSE;
40454 /* The formats are different so we need to do either pre- or post-format conversion. It doesn't matter which. */
40455 pConverter->hasPreFormatConversion = MA_FALSE;
40456 pConverter->hasPostFormatConversion = MA_TRUE;
40459 /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */
40460 if (pConverter->config.formatIn != midFormat) {
40461 pConverter->hasPreFormatConversion = MA_TRUE;
40463 if (pConverter->config.formatOut != midFormat) {
40464 pConverter->hasPostFormatConversion = MA_TRUE;
40468 /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */
40469 if (pConverter->hasPreFormatConversion == MA_FALSE &&
40470 pConverter->hasPostFormatConversion == MA_FALSE &&
40471 pConverter->hasChannelConverter == MA_FALSE &&
40472 pConverter->hasResampler == MA_FALSE) {
40473 pConverter->isPassthrough = MA_TRUE;
40479 MA_API void ma_data_converter_uninit(ma_data_converter* pConverter)
40481 if (pConverter == NULL) {
40485 if (pConverter->hasResampler) {
40486 ma_resampler_uninit(&pConverter->resampler);
40490 static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
40492 ma_uint64 frameCountIn;
40493 ma_uint64 frameCountOut;
40494 ma_uint64 frameCount;
40496 MA_ASSERT(pConverter != NULL);
40499 if (pFrameCountIn != NULL) {
40500 frameCountIn = *pFrameCountIn;
40504 if (pFrameCountOut != NULL) {
40505 frameCountOut = *pFrameCountOut;
40508 frameCount = ma_min(frameCountIn, frameCountOut);
40510 if (pFramesOut != NULL) {
40511 if (pFramesIn != NULL) {
40512 ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
40514 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
40518 if (pFrameCountIn != NULL) {
40519 *pFrameCountIn = frameCount;
40521 if (pFrameCountOut != NULL) {
40522 *pFrameCountOut = frameCount;
40528 static ma_result ma_data_converter_process_pcm_frames__format_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
40530 ma_uint64 frameCountIn;
40531 ma_uint64 frameCountOut;
40532 ma_uint64 frameCount;
40534 MA_ASSERT(pConverter != NULL);
40537 if (pFrameCountIn != NULL) {
40538 frameCountIn = *pFrameCountIn;
40542 if (pFrameCountOut != NULL) {
40543 frameCountOut = *pFrameCountOut;
40546 frameCount = ma_min(frameCountIn, frameCountOut);
40548 if (pFramesOut != NULL) {
40549 if (pFramesIn != NULL) {
40550 ma_convert_pcm_frames_format(pFramesOut, pConverter->config.formatOut, pFramesIn, pConverter->config.formatIn, frameCount, pConverter->config.channelsIn, pConverter->config.ditherMode);
40552 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
40556 if (pFrameCountIn != NULL) {
40557 *pFrameCountIn = frameCount;
40559 if (pFrameCountOut != NULL) {
40560 *pFrameCountOut = frameCount;
40567 static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conversion(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
40569 ma_result result = MA_SUCCESS;
40570 ma_uint64 frameCountIn;
40571 ma_uint64 frameCountOut;
40572 ma_uint64 framesProcessedIn;
40573 ma_uint64 framesProcessedOut;
40575 MA_ASSERT(pConverter != NULL);
40578 if (pFrameCountIn != NULL) {
40579 frameCountIn = *pFrameCountIn;
40583 if (pFrameCountOut != NULL) {
40584 frameCountOut = *pFrameCountOut;
40587 framesProcessedIn = 0;
40588 framesProcessedOut = 0;
40590 while (framesProcessedOut < frameCountOut) {
40591 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
40592 const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
40593 const void* pFramesInThisIteration;
40594 /* */ void* pFramesOutThisIteration;
40595 ma_uint64 frameCountInThisIteration;
40596 ma_uint64 frameCountOutThisIteration;
40598 if (pFramesIn != NULL) {
40599 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn));
40601 pFramesInThisIteration = NULL;
40604 if (pFramesOut != NULL) {
40605 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
40607 pFramesOutThisIteration = NULL;
40610 /* Do a pre format conversion if necessary. */
40611 if (pConverter->hasPreFormatConversion) {
40612 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
40613 const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
40615 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
40616 if (frameCountInThisIteration > tempBufferInCap) {
40617 frameCountInThisIteration = tempBufferInCap;
40620 if (pConverter->hasPostFormatConversion) {
40621 if (frameCountInThisIteration > tempBufferOutCap) {
40622 frameCountInThisIteration = tempBufferOutCap;
40626 if (pFramesInThisIteration != NULL) {
40627 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.config.format, pFramesInThisIteration, pConverter->config.formatIn, frameCountInThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode);
40629 MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
40632 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
40634 if (pConverter->hasPostFormatConversion) {
40635 /* Both input and output conversion required. Output to the temp buffer. */
40636 if (frameCountOutThisIteration > tempBufferOutCap) {
40637 frameCountOutThisIteration = tempBufferOutCap;
40640 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
40642 /* Only pre-format required. Output straight to the output buffer. */
40643 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration);
40646 if (result != MA_SUCCESS) {
40650 /* No pre-format required. Just read straight from the input buffer. */
40651 MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
40653 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
40654 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
40655 if (frameCountOutThisIteration > tempBufferOutCap) {
40656 frameCountOutThisIteration = tempBufferOutCap;
40659 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
40660 if (result != MA_SUCCESS) {
40665 /* If we are doing a post format conversion we need to do that now. */
40666 if (pConverter->hasPostFormatConversion) {
40667 if (pFramesOutThisIteration != NULL) {
40668 ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->config.formatOut, pTempBufferOut, pConverter->resampler.config.format, frameCountOutThisIteration, pConverter->resampler.config.channels, pConverter->config.ditherMode);
40672 framesProcessedIn += frameCountInThisIteration;
40673 framesProcessedOut += frameCountOutThisIteration;
40675 MA_ASSERT(framesProcessedIn <= frameCountIn);
40676 MA_ASSERT(framesProcessedOut <= frameCountOut);
40678 if (frameCountOutThisIteration == 0) {
40679 break; /* Consumed all of our input data. */
40683 if (pFrameCountIn != NULL) {
40684 *pFrameCountIn = framesProcessedIn;
40686 if (pFrameCountOut != NULL) {
40687 *pFrameCountOut = framesProcessedOut;
40693 static ma_result ma_data_converter_process_pcm_frames__resample_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
40695 MA_ASSERT(pConverter != NULL);
40697 if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
40698 /* Neither pre- nor post-format required. This is simple case where only resampling is required. */
40699 return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
40701 /* Format conversion required. */
40702 return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
40706 static ma_result ma_data_converter_process_pcm_frames__channels_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
40709 ma_uint64 frameCountIn;
40710 ma_uint64 frameCountOut;
40711 ma_uint64 frameCount;
40713 MA_ASSERT(pConverter != NULL);
40716 if (pFrameCountIn != NULL) {
40717 frameCountIn = *pFrameCountIn;
40721 if (pFrameCountOut != NULL) {
40722 frameCountOut = *pFrameCountOut;
40725 frameCount = ma_min(frameCountIn, frameCountOut);
40727 if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
40728 /* No format conversion required. */
40729 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount);
40730 if (result != MA_SUCCESS) {
40734 /* Format conversion required. */
40735 ma_uint64 framesProcessed = 0;
40737 while (framesProcessed < frameCount) {
40738 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
40739 const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
40740 const void* pFramesInThisIteration;
40741 /* */ void* pFramesOutThisIteration;
40742 ma_uint64 frameCountThisIteration;
40744 if (pFramesIn != NULL) {
40745 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn));
40747 pFramesInThisIteration = NULL;
40750 if (pFramesOut != NULL) {
40751 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
40753 pFramesOutThisIteration = NULL;
40756 /* Do a pre format conversion if necessary. */
40757 if (pConverter->hasPreFormatConversion) {
40758 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
40759 const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
40761 frameCountThisIteration = (frameCount - framesProcessed);
40762 if (frameCountThisIteration > tempBufferInCap) {
40763 frameCountThisIteration = tempBufferInCap;
40766 if (pConverter->hasPostFormatConversion) {
40767 if (frameCountThisIteration > tempBufferOutCap) {
40768 frameCountThisIteration = tempBufferOutCap;
40772 if (pFramesInThisIteration != NULL) {
40773 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->config.formatIn, frameCountThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode);
40775 MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
40778 if (pConverter->hasPostFormatConversion) {
40779 /* Both input and output conversion required. Output to the temp buffer. */
40780 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration);
40782 /* Only pre-format required. Output straight to the output buffer. */
40783 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration);
40786 if (result != MA_SUCCESS) {
40790 /* No pre-format required. Just read straight from the input buffer. */
40791 MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
40793 frameCountThisIteration = (frameCount - framesProcessed);
40794 if (frameCountThisIteration > tempBufferOutCap) {
40795 frameCountThisIteration = tempBufferOutCap;
40798 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration);
40799 if (result != MA_SUCCESS) {
40804 /* If we are doing a post format conversion we need to do that now. */
40805 if (pConverter->hasPostFormatConversion) {
40806 if (pFramesOutThisIteration != NULL) {
40807 ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->config.formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->config.ditherMode);
40811 framesProcessed += frameCountThisIteration;
40815 if (pFrameCountIn != NULL) {
40816 *pFrameCountIn = frameCount;
40818 if (pFrameCountOut != NULL) {
40819 *pFrameCountOut = frameCount;
40825 static 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)
40828 ma_uint64 frameCountIn;
40829 ma_uint64 frameCountOut;
40830 ma_uint64 framesProcessedIn;
40831 ma_uint64 framesProcessedOut;
40832 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */
40833 ma_uint64 tempBufferInCap;
40834 ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */
40835 ma_uint64 tempBufferMidCap;
40836 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */
40837 ma_uint64 tempBufferOutCap;
40839 MA_ASSERT(pConverter != NULL);
40840 MA_ASSERT(pConverter->resampler.config.format == pConverter->channelConverter.format);
40841 MA_ASSERT(pConverter->resampler.config.channels == pConverter->channelConverter.channelsIn);
40842 MA_ASSERT(pConverter->resampler.config.channels < pConverter->channelConverter.channelsOut);
40845 if (pFrameCountIn != NULL) {
40846 frameCountIn = *pFrameCountIn;
40850 if (pFrameCountOut != NULL) {
40851 frameCountOut = *pFrameCountOut;
40854 framesProcessedIn = 0;
40855 framesProcessedOut = 0;
40857 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
40858 tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
40859 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
40861 while (framesProcessedOut < frameCountOut) {
40862 ma_uint64 frameCountInThisIteration;
40863 ma_uint64 frameCountOutThisIteration;
40864 const void* pRunningFramesIn = NULL;
40865 void* pRunningFramesOut = NULL;
40866 const void* pResampleBufferIn;
40867 void* pChannelsBufferOut;
40869 if (pFramesIn != NULL) {
40870 pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn));
40872 if (pFramesOut != NULL) {
40873 pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
40876 /* Run input data through the resampler and output it to the temporary buffer. */
40877 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
40879 if (pConverter->hasPreFormatConversion) {
40880 if (frameCountInThisIteration > tempBufferInCap) {
40881 frameCountInThisIteration = tempBufferInCap;
40885 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
40886 if (frameCountOutThisIteration > tempBufferMidCap) {
40887 frameCountOutThisIteration = tempBufferMidCap;
40890 /* We can't read more frames than can fit in the output buffer. */
40891 if (pConverter->hasPostFormatConversion) {
40892 if (frameCountOutThisIteration > tempBufferOutCap) {
40893 frameCountOutThisIteration = tempBufferOutCap;
40897 /* 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. */
40899 ma_uint64 requiredInputFrameCount = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration);
40900 if (frameCountInThisIteration > requiredInputFrameCount) {
40901 frameCountInThisIteration = requiredInputFrameCount;
40905 if (pConverter->hasPreFormatConversion) {
40906 if (pFramesIn != NULL) {
40907 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.config.format, pRunningFramesIn, pConverter->config.formatIn, frameCountInThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode);
40908 pResampleBufferIn = pTempBufferIn;
40910 pResampleBufferIn = NULL;
40913 pResampleBufferIn = pRunningFramesIn;
40916 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration);
40917 if (result != MA_SUCCESS) {
40923 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
40924 this part if we have an output buffer.
40926 if (pFramesOut != NULL) {
40927 if (pConverter->hasPostFormatConversion) {
40928 pChannelsBufferOut = pTempBufferOut;
40930 pChannelsBufferOut = pRunningFramesOut;
40933 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration);
40934 if (result != MA_SUCCESS) {
40938 /* Finally we do post format conversion. */
40939 if (pConverter->hasPostFormatConversion) {
40940 ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->config.formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->config.ditherMode);
40945 framesProcessedIn += frameCountInThisIteration;
40946 framesProcessedOut += frameCountOutThisIteration;
40948 MA_ASSERT(framesProcessedIn <= frameCountIn);
40949 MA_ASSERT(framesProcessedOut <= frameCountOut);
40951 if (frameCountOutThisIteration == 0) {
40952 break; /* Consumed all of our input data. */
40956 if (pFrameCountIn != NULL) {
40957 *pFrameCountIn = framesProcessedIn;
40959 if (pFrameCountOut != NULL) {
40960 *pFrameCountOut = framesProcessedOut;
40966 static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
40969 ma_uint64 frameCountIn;
40970 ma_uint64 frameCountOut;
40971 ma_uint64 framesProcessedIn;
40972 ma_uint64 framesProcessedOut;
40973 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */
40974 ma_uint64 tempBufferInCap;
40975 ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */
40976 ma_uint64 tempBufferMidCap;
40977 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */
40978 ma_uint64 tempBufferOutCap;
40980 MA_ASSERT(pConverter != NULL);
40981 MA_ASSERT(pConverter->resampler.config.format == pConverter->channelConverter.format);
40982 MA_ASSERT(pConverter->resampler.config.channels == pConverter->channelConverter.channelsOut);
40983 MA_ASSERT(pConverter->resampler.config.channels < pConverter->channelConverter.channelsIn);
40986 if (pFrameCountIn != NULL) {
40987 frameCountIn = *pFrameCountIn;
40991 if (pFrameCountOut != NULL) {
40992 frameCountOut = *pFrameCountOut;
40995 framesProcessedIn = 0;
40996 framesProcessedOut = 0;
40998 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
40999 tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
41000 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
41002 while (framesProcessedOut < frameCountOut) {
41003 ma_uint64 frameCountInThisIteration;
41004 ma_uint64 frameCountOutThisIteration;
41005 const void* pRunningFramesIn = NULL;
41006 void* pRunningFramesOut = NULL;
41007 const void* pChannelsBufferIn;
41008 void* pResampleBufferOut;
41010 if (pFramesIn != NULL) {
41011 pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn));
41013 if (pFramesOut != NULL) {
41014 pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
41017 /* Run input data through the channel converter and output it to the temporary buffer. */
41018 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
41020 if (pConverter->hasPreFormatConversion) {
41021 if (frameCountInThisIteration > tempBufferInCap) {
41022 frameCountInThisIteration = tempBufferInCap;
41025 if (pRunningFramesIn != NULL) {
41026 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->config.formatIn, frameCountInThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode);
41027 pChannelsBufferIn = pTempBufferIn;
41029 pChannelsBufferIn = NULL;
41032 pChannelsBufferIn = pRunningFramesIn;
41036 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
41037 in this case which means we should always have capacity, but I'm leaving it here just for safety for future maintenance.
41039 if (frameCountInThisIteration > tempBufferMidCap) {
41040 frameCountInThisIteration = tempBufferMidCap;
41044 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
41045 input samples and will end up glitching.
41047 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
41048 if (frameCountOutThisIteration > tempBufferMidCap) {
41049 frameCountOutThisIteration = tempBufferMidCap;
41052 if (pConverter->hasPostFormatConversion) {
41053 ma_uint64 requiredInputFrameCount;
41055 if (frameCountOutThisIteration > tempBufferOutCap) {
41056 frameCountOutThisIteration = tempBufferOutCap;
41059 requiredInputFrameCount = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration);
41060 if (frameCountInThisIteration > requiredInputFrameCount) {
41061 frameCountInThisIteration = requiredInputFrameCount;
41065 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration);
41066 if (result != MA_SUCCESS) {
41071 /* At this point we have converted the channels to the output channel count which we now need to resample. */
41072 if (pConverter->hasPostFormatConversion) {
41073 pResampleBufferOut = pTempBufferOut;
41075 pResampleBufferOut = pRunningFramesOut;
41078 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration);
41079 if (result != MA_SUCCESS) {
41083 /* Finally we can do the post format conversion. */
41084 if (pConverter->hasPostFormatConversion) {
41085 if (pRunningFramesOut != NULL) {
41086 ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->config.formatOut, pResampleBufferOut, pConverter->resampler.config.format, frameCountOutThisIteration, pConverter->config.channelsOut, pConverter->config.ditherMode);
41090 framesProcessedIn += frameCountInThisIteration;
41091 framesProcessedOut += frameCountOutThisIteration;
41093 MA_ASSERT(framesProcessedIn <= frameCountIn);
41094 MA_ASSERT(framesProcessedOut <= frameCountOut);
41096 if (frameCountOutThisIteration == 0) {
41097 break; /* Consumed all of our input data. */
41101 if (pFrameCountIn != NULL) {
41102 *pFrameCountIn = framesProcessedIn;
41104 if (pFrameCountOut != NULL) {
41105 *pFrameCountOut = framesProcessedOut;
41111 MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
41113 if (pConverter == NULL) {
41114 return MA_INVALID_ARGS;
41117 if (pConverter->isPassthrough) {
41118 return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
41122 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
41123 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
41124 that it has less work (resampling is the most expensive part of format conversion).
41126 if (pConverter->config.channelsIn < pConverter->config.channelsOut) {
41127 /* Do resampling first, if necessary. */
41128 MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE);
41130 if (pConverter->hasResampler) {
41131 /* Resampling first. */
41132 return ma_data_converter_process_pcm_frames__resampling_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
41134 /* Resampling not required. */
41135 return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
41138 /* Do channel conversion first, if necessary. */
41139 if (pConverter->hasChannelConverter) {
41140 if (pConverter->hasResampler) {
41141 /* Channel routing first. */
41142 return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
41144 /* Resampling not required. */
41145 return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
41148 /* Channel routing not required. */
41149 if (pConverter->hasResampler) {
41150 /* Resampling only. */
41151 return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
41153 /* No channel routing nor resampling required. Just format conversion. */
41154 return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
41160 MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
41162 if (pConverter == NULL) {
41163 return MA_INVALID_ARGS;
41166 if (pConverter->hasResampler == MA_FALSE) {
41167 return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */
41170 return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut);
41173 MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut)
41175 if (pConverter == NULL) {
41176 return MA_INVALID_ARGS;
41179 if (pConverter->hasResampler == MA_FALSE) {
41180 return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */
41183 return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut);
41186 MA_API ma_uint64 ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount)
41188 if (pConverter == NULL) {
41192 if (pConverter->hasResampler) {
41193 return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount);
41195 return outputFrameCount; /* 1:1 */
41199 MA_API ma_uint64 ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount)
41201 if (pConverter == NULL) {
41205 if (pConverter->hasResampler) {
41206 return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount);
41208 return inputFrameCount; /* 1:1 */
41212 MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter)
41214 if (pConverter == NULL) {
41218 if (pConverter->hasResampler) {
41219 return ma_resampler_get_input_latency(&pConverter->resampler);
41222 return 0; /* No latency without a resampler. */
41225 MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter)
41227 if (pConverter == NULL) {
41231 if (pConverter->hasResampler) {
41232 return ma_resampler_get_output_latency(&pConverter->resampler);
41235 return 0; /* No latency without a resampler. */
41240 /**************************************************************************************************************************************************************
41244 **************************************************************************************************************************************************************/
41245 MA_API void ma_channel_map_init_blank(ma_uint32 channels, ma_channel* pChannelMap)
41247 if (pChannelMap == NULL) {
41251 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels);
41254 static void ma_get_standard_channel_map_microsoft(ma_uint32 channels, ma_channel* pChannelMap)
41256 /* Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */
41261 pChannelMap[0] = MA_CHANNEL_MONO;
41266 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41267 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41270 case 3: /* Not defined, but best guess. */
41272 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41273 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41274 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
41279 #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP
41280 /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */
41281 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41282 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41283 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
41284 pChannelMap[3] = MA_CHANNEL_BACK_CENTER;
41287 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41288 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41289 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
41290 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
41294 case 5: /* Not defined, but best guess. */
41296 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41297 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41298 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
41299 pChannelMap[3] = MA_CHANNEL_BACK_LEFT;
41300 pChannelMap[4] = MA_CHANNEL_BACK_RIGHT;
41305 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41306 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41307 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
41308 pChannelMap[3] = MA_CHANNEL_LFE;
41309 pChannelMap[4] = MA_CHANNEL_SIDE_LEFT;
41310 pChannelMap[5] = MA_CHANNEL_SIDE_RIGHT;
41313 case 7: /* Not defined, but best guess. */
41315 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41316 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41317 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
41318 pChannelMap[3] = MA_CHANNEL_LFE;
41319 pChannelMap[4] = MA_CHANNEL_BACK_CENTER;
41320 pChannelMap[5] = MA_CHANNEL_SIDE_LEFT;
41321 pChannelMap[6] = MA_CHANNEL_SIDE_RIGHT;
41327 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41328 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41329 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
41330 pChannelMap[3] = MA_CHANNEL_LFE;
41331 pChannelMap[4] = MA_CHANNEL_BACK_LEFT;
41332 pChannelMap[5] = MA_CHANNEL_BACK_RIGHT;
41333 pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
41334 pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
41339 if (channels > 8) {
41340 ma_uint32 iChannel;
41341 for (iChannel = 8; iChannel < channels; ++iChannel) {
41342 if (iChannel < MA_MAX_CHANNELS) {
41343 pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
41345 pChannelMap[iChannel] = MA_CHANNEL_NONE;
41351 static void ma_get_standard_channel_map_alsa(ma_uint32 channels, ma_channel* pChannelMap)
41357 pChannelMap[0] = MA_CHANNEL_MONO;
41362 pChannelMap[0] = MA_CHANNEL_LEFT;
41363 pChannelMap[1] = MA_CHANNEL_RIGHT;
41368 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41369 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41370 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
41375 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41376 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41377 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
41378 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
41383 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41384 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41385 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
41386 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
41387 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
41392 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41393 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41394 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
41395 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
41396 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
41397 pChannelMap[5] = MA_CHANNEL_LFE;
41402 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41403 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41404 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
41405 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
41406 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
41407 pChannelMap[5] = MA_CHANNEL_LFE;
41408 pChannelMap[6] = MA_CHANNEL_BACK_CENTER;
41414 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41415 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41416 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
41417 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
41418 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
41419 pChannelMap[5] = MA_CHANNEL_LFE;
41420 pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
41421 pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
41426 if (channels > 8) {
41427 ma_uint32 iChannel;
41428 for (iChannel = 8; iChannel < channels; ++iChannel) {
41429 if (iChannel < MA_MAX_CHANNELS) {
41430 pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
41432 pChannelMap[iChannel] = MA_CHANNEL_NONE;
41438 static void ma_get_standard_channel_map_rfc3551(ma_uint32 channels, ma_channel* pChannelMap)
41444 pChannelMap[0] = MA_CHANNEL_MONO;
41449 pChannelMap[0] = MA_CHANNEL_LEFT;
41450 pChannelMap[1] = MA_CHANNEL_RIGHT;
41455 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41456 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41457 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
41462 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41463 pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
41464 pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
41465 pChannelMap[3] = MA_CHANNEL_BACK_CENTER;
41470 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41471 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41472 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
41473 pChannelMap[3] = MA_CHANNEL_BACK_LEFT;
41474 pChannelMap[4] = MA_CHANNEL_BACK_RIGHT;
41479 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41480 pChannelMap[1] = MA_CHANNEL_SIDE_LEFT;
41481 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
41482 pChannelMap[3] = MA_CHANNEL_FRONT_RIGHT;
41483 pChannelMap[4] = MA_CHANNEL_SIDE_RIGHT;
41484 pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
41489 if (channels > 8) {
41490 ma_uint32 iChannel;
41491 for (iChannel = 6; iChannel < channels; ++iChannel) {
41492 if (iChannel < MA_MAX_CHANNELS) {
41493 pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6));
41495 pChannelMap[iChannel] = MA_CHANNEL_NONE;
41501 static void ma_get_standard_channel_map_flac(ma_uint32 channels, ma_channel* pChannelMap)
41507 pChannelMap[0] = MA_CHANNEL_MONO;
41512 pChannelMap[0] = MA_CHANNEL_LEFT;
41513 pChannelMap[1] = MA_CHANNEL_RIGHT;
41518 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41519 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41520 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
41525 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41526 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41527 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
41528 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
41533 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41534 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41535 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
41536 pChannelMap[3] = MA_CHANNEL_BACK_LEFT;
41537 pChannelMap[4] = MA_CHANNEL_BACK_RIGHT;
41542 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41543 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41544 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
41545 pChannelMap[3] = MA_CHANNEL_LFE;
41546 pChannelMap[4] = MA_CHANNEL_BACK_LEFT;
41547 pChannelMap[5] = MA_CHANNEL_BACK_RIGHT;
41552 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41553 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41554 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
41555 pChannelMap[3] = MA_CHANNEL_LFE;
41556 pChannelMap[4] = MA_CHANNEL_BACK_CENTER;
41557 pChannelMap[5] = MA_CHANNEL_SIDE_LEFT;
41558 pChannelMap[6] = MA_CHANNEL_SIDE_RIGHT;
41564 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41565 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41566 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
41567 pChannelMap[3] = MA_CHANNEL_LFE;
41568 pChannelMap[4] = MA_CHANNEL_BACK_LEFT;
41569 pChannelMap[5] = MA_CHANNEL_BACK_RIGHT;
41570 pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
41571 pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
41576 if (channels > 8) {
41577 ma_uint32 iChannel;
41578 for (iChannel = 8; iChannel < channels; ++iChannel) {
41579 if (iChannel < MA_MAX_CHANNELS) {
41580 pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
41582 pChannelMap[iChannel] = MA_CHANNEL_NONE;
41588 static void ma_get_standard_channel_map_vorbis(ma_uint32 channels, ma_channel* pChannelMap)
41590 /* 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?! */
41595 pChannelMap[0] = MA_CHANNEL_MONO;
41600 pChannelMap[0] = MA_CHANNEL_LEFT;
41601 pChannelMap[1] = MA_CHANNEL_RIGHT;
41606 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41607 pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
41608 pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
41613 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41614 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41615 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
41616 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
41621 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41622 pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
41623 pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
41624 pChannelMap[3] = MA_CHANNEL_BACK_LEFT;
41625 pChannelMap[4] = MA_CHANNEL_BACK_RIGHT;
41630 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41631 pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
41632 pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
41633 pChannelMap[3] = MA_CHANNEL_BACK_LEFT;
41634 pChannelMap[4] = MA_CHANNEL_BACK_RIGHT;
41635 pChannelMap[5] = MA_CHANNEL_LFE;
41640 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41641 pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
41642 pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
41643 pChannelMap[3] = MA_CHANNEL_SIDE_LEFT;
41644 pChannelMap[4] = MA_CHANNEL_SIDE_RIGHT;
41645 pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
41646 pChannelMap[6] = MA_CHANNEL_LFE;
41652 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41653 pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
41654 pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
41655 pChannelMap[3] = MA_CHANNEL_SIDE_LEFT;
41656 pChannelMap[4] = MA_CHANNEL_SIDE_RIGHT;
41657 pChannelMap[5] = MA_CHANNEL_BACK_LEFT;
41658 pChannelMap[6] = MA_CHANNEL_BACK_RIGHT;
41659 pChannelMap[7] = MA_CHANNEL_LFE;
41664 if (channels > 8) {
41665 ma_uint32 iChannel;
41666 for (iChannel = 8; iChannel < channels; ++iChannel) {
41667 if (iChannel < MA_MAX_CHANNELS) {
41668 pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
41670 pChannelMap[iChannel] = MA_CHANNEL_NONE;
41676 static void ma_get_standard_channel_map_sound4(ma_uint32 channels, ma_channel* pChannelMap)
41682 pChannelMap[0] = MA_CHANNEL_MONO;
41687 pChannelMap[0] = MA_CHANNEL_LEFT;
41688 pChannelMap[1] = MA_CHANNEL_RIGHT;
41693 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41694 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41695 pChannelMap[2] = MA_CHANNEL_BACK_CENTER;
41700 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41701 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41702 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
41703 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
41708 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41709 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41710 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
41711 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
41712 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
41717 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41718 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41719 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
41720 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
41721 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
41722 pChannelMap[5] = MA_CHANNEL_LFE;
41727 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41728 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41729 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
41730 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
41731 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
41732 pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
41733 pChannelMap[6] = MA_CHANNEL_LFE;
41739 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41740 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41741 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
41742 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
41743 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
41744 pChannelMap[5] = MA_CHANNEL_LFE;
41745 pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
41746 pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
41751 if (channels > 8) {
41752 ma_uint32 iChannel;
41753 for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
41754 if (iChannel < MA_MAX_CHANNELS) {
41755 pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
41757 pChannelMap[iChannel] = MA_CHANNEL_NONE;
41763 static void ma_get_standard_channel_map_sndio(ma_uint32 channels, ma_channel* pChannelMap)
41769 pChannelMap[0] = MA_CHANNEL_MONO;
41774 pChannelMap[0] = MA_CHANNEL_LEFT;
41775 pChannelMap[1] = MA_CHANNEL_RIGHT;
41780 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41781 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41782 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
41787 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41788 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41789 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
41790 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
41795 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41796 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41797 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
41798 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
41799 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
41805 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
41806 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
41807 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
41808 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
41809 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
41810 pChannelMap[5] = MA_CHANNEL_LFE;
41815 if (channels > 6) {
41816 ma_uint32 iChannel;
41817 for (iChannel = 6; iChannel < channels && iChannel < MA_MAX_CHANNELS; ++iChannel) {
41818 if (iChannel < MA_MAX_CHANNELS) {
41819 pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6));
41821 pChannelMap[iChannel] = MA_CHANNEL_NONE;
41827 MA_API void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel* pChannelMap)
41829 switch (standardChannelMap)
41831 case ma_standard_channel_map_alsa:
41833 ma_get_standard_channel_map_alsa(channels, pChannelMap);
41836 case ma_standard_channel_map_rfc3551:
41838 ma_get_standard_channel_map_rfc3551(channels, pChannelMap);
41841 case ma_standard_channel_map_flac:
41843 ma_get_standard_channel_map_flac(channels, pChannelMap);
41846 case ma_standard_channel_map_vorbis:
41848 ma_get_standard_channel_map_vorbis(channels, pChannelMap);
41851 case ma_standard_channel_map_sound4:
41853 ma_get_standard_channel_map_sound4(channels, pChannelMap);
41856 case ma_standard_channel_map_sndio:
41858 ma_get_standard_channel_map_sndio(channels, pChannelMap);
41861 case ma_standard_channel_map_microsoft: /* Also default. */
41862 /*case ma_standard_channel_map_default;*/
41865 ma_get_standard_channel_map_microsoft(channels, pChannelMap);
41870 MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels)
41872 if (pOut != NULL && pIn != NULL && channels > 0) {
41873 MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels);
41877 MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels)
41879 if (pOut == NULL || channels == 0) {
41884 ma_channel_map_copy(pOut, pIn, channels);
41886 ma_get_standard_channel_map(ma_standard_channel_map_default, channels, pOut);
41890 MA_API ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel* pChannelMap)
41892 if (pChannelMap == NULL) {
41896 /* A channel count of 0 is invalid. */
41897 if (channels == 0) {
41901 /* It does not make sense to have a mono channel when there is more than 1 channel. */
41902 if (channels > 1) {
41903 ma_uint32 iChannel;
41904 for (iChannel = 0; iChannel < channels; ++iChannel) {
41905 if (pChannelMap[iChannel] == MA_CHANNEL_MONO) {
41914 MA_API ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel* pChannelMapA, const ma_channel* pChannelMapB)
41916 ma_uint32 iChannel;
41918 if (pChannelMapA == pChannelMapB) {
41922 for (iChannel = 0; iChannel < channels; ++iChannel) {
41923 if (pChannelMapA[iChannel] != pChannelMapB[iChannel]) {
41931 MA_API ma_bool32 ma_channel_map_blank(ma_uint32 channels, const ma_channel* pChannelMap)
41933 ma_uint32 iChannel;
41935 for (iChannel = 0; iChannel < channels; ++iChannel) {
41936 if (pChannelMap[iChannel] != MA_CHANNEL_NONE) {
41944 MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition)
41946 ma_uint32 iChannel;
41948 for (iChannel = 0; iChannel < channels; ++iChannel) {
41949 if (pChannelMap[iChannel] == channelPosition) {
41959 /**************************************************************************************************************************************************************
41963 **************************************************************************************************************************************************************/
41964 MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn)
41966 ma_data_converter_config config;
41968 config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut);
41969 ma_get_standard_channel_map(ma_standard_channel_map_default, channelsOut, config.channelMapOut);
41970 ma_get_standard_channel_map(ma_standard_channel_map_default, channelsIn, config.channelMapIn);
41971 config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
41973 return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config);
41976 MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig)
41979 ma_data_converter converter;
41981 if (frameCountIn == 0 || pConfig == NULL) {
41985 result = ma_data_converter_init(pConfig, &converter);
41986 if (result != MA_SUCCESS) {
41987 return 0; /* Failed to initialize the data converter. */
41990 if (pOut == NULL) {
41991 frameCountOut = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn);
41993 result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut);
41994 if (result != MA_SUCCESS) {
41999 ma_data_converter_uninit(&converter);
42000 return frameCountOut;
42004 /**************************************************************************************************************************************************************
42008 **************************************************************************************************************************************************************/
42009 static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset)
42011 return encodedOffset & 0x7FFFFFFF;
42014 static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset)
42016 return encodedOffset & 0x80000000;
42019 static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB)
42021 MA_ASSERT(pRB != NULL);
42022 return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedReadOffset)));
42025 static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB)
42027 MA_ASSERT(pRB != NULL);
42028 return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedWriteOffset)));
42031 static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag)
42033 return offsetLoopFlag | offsetInBytes;
42036 static MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag)
42038 MA_ASSERT(pOffsetInBytes != NULL);
42039 MA_ASSERT(pOffsetLoopFlag != NULL);
42041 *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset);
42042 *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset);
42046 MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB)
42049 const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1);
42052 return MA_INVALID_ARGS;
42055 if (subbufferSizeInBytes == 0 || subbufferCount == 0) {
42056 return MA_INVALID_ARGS;
42059 if (subbufferSizeInBytes > maxSubBufferSize) {
42060 return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */
42064 MA_ZERO_OBJECT(pRB);
42066 result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks);
42067 if (result != MA_SUCCESS) {
42071 pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes;
42072 pRB->subbufferCount = (ma_uint32)subbufferCount;
42074 if (pOptionalPreallocatedBuffer != NULL) {
42075 pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes;
42076 pRB->pBuffer = pOptionalPreallocatedBuffer;
42078 size_t bufferSizeInBytes;
42081 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
42082 we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT.
42084 pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT;
42086 bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes;
42087 pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks);
42088 if (pRB->pBuffer == NULL) {
42089 return MA_OUT_OF_MEMORY;
42092 MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes);
42093 pRB->ownsBuffer = MA_TRUE;
42099 MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB)
42101 return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
42104 MA_API void ma_rb_uninit(ma_rb* pRB)
42110 if (pRB->ownsBuffer) {
42111 ma_aligned_free(pRB->pBuffer, &pRB->allocationCallbacks);
42115 MA_API void ma_rb_reset(ma_rb* pRB)
42121 c89atomic_exchange_32(&pRB->encodedReadOffset, 0);
42122 c89atomic_exchange_32(&pRB->encodedWriteOffset, 0);
42125 MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
42127 ma_uint32 writeOffset;
42128 ma_uint32 writeOffsetInBytes;
42129 ma_uint32 writeOffsetLoopFlag;
42130 ma_uint32 readOffset;
42131 ma_uint32 readOffsetInBytes;
42132 ma_uint32 readOffsetLoopFlag;
42133 size_t bytesAvailable;
42134 size_t bytesRequested;
42136 if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
42137 return MA_INVALID_ARGS;
42140 /* The returned buffer should never move ahead of the write pointer. */
42141 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
42142 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
42144 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
42145 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
42148 The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we
42149 can only read up to the write pointer. If not, we can only read up to the end of the buffer.
42151 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
42152 bytesAvailable = writeOffsetInBytes - readOffsetInBytes;
42154 bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes;
42157 bytesRequested = *pSizeInBytes;
42158 if (bytesRequested > bytesAvailable) {
42159 bytesRequested = bytesAvailable;
42162 *pSizeInBytes = bytesRequested;
42163 (*ppBufferOut) = ma_rb__get_read_ptr(pRB);
42168 MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut)
42170 ma_uint32 readOffset;
42171 ma_uint32 readOffsetInBytes;
42172 ma_uint32 readOffsetLoopFlag;
42173 ma_uint32 newReadOffsetInBytes;
42174 ma_uint32 newReadOffsetLoopFlag;
42177 return MA_INVALID_ARGS;
42180 /* Validate the buffer. */
42181 if (pBufferOut != ma_rb__get_read_ptr(pRB)) {
42182 return MA_INVALID_ARGS;
42185 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
42186 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
42188 /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
42189 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes);
42190 if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) {
42191 return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
42194 /* Move the read pointer back to the start if necessary. */
42195 newReadOffsetLoopFlag = readOffsetLoopFlag;
42196 if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) {
42197 newReadOffsetInBytes = 0;
42198 newReadOffsetLoopFlag ^= 0x80000000;
42201 c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes));
42205 MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
42207 ma_uint32 readOffset;
42208 ma_uint32 readOffsetInBytes;
42209 ma_uint32 readOffsetLoopFlag;
42210 ma_uint32 writeOffset;
42211 ma_uint32 writeOffsetInBytes;
42212 ma_uint32 writeOffsetLoopFlag;
42213 size_t bytesAvailable;
42214 size_t bytesRequested;
42216 if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
42217 return MA_INVALID_ARGS;
42220 /* The returned buffer should never overtake the read buffer. */
42221 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
42222 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
42224 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
42225 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
42228 In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only
42229 write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should
42230 never overtake the read pointer.
42232 if (writeOffsetLoopFlag == readOffsetLoopFlag) {
42233 bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes;
42235 bytesAvailable = readOffsetInBytes - writeOffsetInBytes;
42238 bytesRequested = *pSizeInBytes;
42239 if (bytesRequested > bytesAvailable) {
42240 bytesRequested = bytesAvailable;
42243 *pSizeInBytes = bytesRequested;
42244 *ppBufferOut = ma_rb__get_write_ptr(pRB);
42246 /* Clear the buffer if desired. */
42247 if (pRB->clearOnWriteAcquire) {
42248 MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes);
42254 MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut)
42256 ma_uint32 writeOffset;
42257 ma_uint32 writeOffsetInBytes;
42258 ma_uint32 writeOffsetLoopFlag;
42259 ma_uint32 newWriteOffsetInBytes;
42260 ma_uint32 newWriteOffsetLoopFlag;
42263 return MA_INVALID_ARGS;
42266 /* Validate the buffer. */
42267 if (pBufferOut != ma_rb__get_write_ptr(pRB)) {
42268 return MA_INVALID_ARGS;
42271 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
42272 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
42274 /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
42275 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes);
42276 if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) {
42277 return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
42280 /* Move the read pointer back to the start if necessary. */
42281 newWriteOffsetLoopFlag = writeOffsetLoopFlag;
42282 if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) {
42283 newWriteOffsetInBytes = 0;
42284 newWriteOffsetLoopFlag ^= 0x80000000;
42287 c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes));
42291 MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes)
42293 ma_uint32 readOffset;
42294 ma_uint32 readOffsetInBytes;
42295 ma_uint32 readOffsetLoopFlag;
42296 ma_uint32 writeOffset;
42297 ma_uint32 writeOffsetInBytes;
42298 ma_uint32 writeOffsetLoopFlag;
42299 ma_uint32 newReadOffsetInBytes;
42300 ma_uint32 newReadOffsetLoopFlag;
42302 if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) {
42303 return MA_INVALID_ARGS;
42306 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
42307 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
42309 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
42310 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
42312 newReadOffsetLoopFlag = readOffsetLoopFlag;
42314 /* We cannot go past the write buffer. */
42315 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
42316 if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) {
42317 newReadOffsetInBytes = writeOffsetInBytes;
42319 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
42322 /* May end up looping. */
42323 if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
42324 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
42325 newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
42327 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
42331 c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag));
42335 MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes)
42337 ma_uint32 readOffset;
42338 ma_uint32 readOffsetInBytes;
42339 ma_uint32 readOffsetLoopFlag;
42340 ma_uint32 writeOffset;
42341 ma_uint32 writeOffsetInBytes;
42342 ma_uint32 writeOffsetLoopFlag;
42343 ma_uint32 newWriteOffsetInBytes;
42344 ma_uint32 newWriteOffsetLoopFlag;
42347 return MA_INVALID_ARGS;
42350 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
42351 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
42353 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
42354 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
42356 newWriteOffsetLoopFlag = writeOffsetLoopFlag;
42358 /* We cannot go past the write buffer. */
42359 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
42360 /* May end up looping. */
42361 if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
42362 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
42363 newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
42365 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
42368 if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) {
42369 newWriteOffsetInBytes = readOffsetInBytes;
42371 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
42375 c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag));
42379 MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB)
42381 ma_uint32 readOffset;
42382 ma_uint32 readOffsetInBytes;
42383 ma_uint32 readOffsetLoopFlag;
42384 ma_uint32 writeOffset;
42385 ma_uint32 writeOffsetInBytes;
42386 ma_uint32 writeOffsetLoopFlag;
42392 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
42393 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
42395 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
42396 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
42398 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
42399 return writeOffsetInBytes - readOffsetInBytes;
42401 return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes);
42405 MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB)
42413 dist = ma_rb_pointer_distance(pRB);
42421 MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB)
42427 return (ma_uint32)(ma_rb_get_subbuffer_size(pRB) - ma_rb_pointer_distance(pRB));
42430 MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB)
42436 return pRB->subbufferSizeInBytes;
42439 MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB)
42445 if (pRB->subbufferStrideInBytes == 0) {
42446 return (size_t)pRB->subbufferSizeInBytes;
42449 return (size_t)pRB->subbufferStrideInBytes;
42452 MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex)
42458 return subbufferIndex * ma_rb_get_subbuffer_stride(pRB);
42461 MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer)
42467 return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex));
42472 static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB)
42474 MA_ASSERT(pRB != NULL);
42476 return ma_get_bytes_per_frame(pRB->format, pRB->channels);
42479 MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB)
42485 return MA_INVALID_ARGS;
42488 MA_ZERO_OBJECT(pRB);
42490 bpf = ma_get_bytes_per_frame(format, channels);
42492 return MA_INVALID_ARGS;
42495 result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb);
42496 if (result != MA_SUCCESS) {
42500 pRB->format = format;
42501 pRB->channels = channels;
42506 MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB)
42508 return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
42511 MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB)
42517 ma_rb_uninit(&pRB->rb);
42520 MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB)
42526 ma_rb_reset(&pRB->rb);
42529 MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
42531 size_t sizeInBytes;
42534 if (pRB == NULL || pSizeInFrames == NULL) {
42535 return MA_INVALID_ARGS;
42538 sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
42540 result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut);
42541 if (result != MA_SUCCESS) {
42545 *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB));
42549 MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut)
42552 return MA_INVALID_ARGS;
42555 return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB), pBufferOut);
42558 MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
42560 size_t sizeInBytes;
42564 return MA_INVALID_ARGS;
42567 sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
42569 result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut);
42570 if (result != MA_SUCCESS) {
42574 *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB));
42578 MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut)
42581 return MA_INVALID_ARGS;
42584 return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB), pBufferOut);
42587 MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
42590 return MA_INVALID_ARGS;
42593 return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
42596 MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
42599 return MA_INVALID_ARGS;
42602 return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
42605 MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB)
42611 return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
42614 MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB)
42620 return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
42623 MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB)
42629 return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
42632 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB)
42638 return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
42641 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB)
42647 return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
42650 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex)
42656 return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB));
42659 MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer)
42665 return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer);
42670 MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB)
42673 ma_uint32 sizeInFrames;
42675 sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5);
42676 if (sizeInFrames == 0) {
42677 return MA_INVALID_ARGS;
42680 result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb);
42681 if (result != MA_SUCCESS) {
42685 /* Seek forward a bit so we have a bit of a buffer in case of desyncs. */
42686 ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2);
42691 MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB)
42693 ma_pcm_rb_uninit((ma_pcm_rb*)pRB);
42699 /**************************************************************************************************************************************************************
42701 Miscellaneous Helpers
42703 **************************************************************************************************************************************************************/
42704 MA_API const char* ma_result_description(ma_result result)
42708 case MA_SUCCESS: return "No error";
42709 case MA_ERROR: return "Unknown error";
42710 case MA_INVALID_ARGS: return "Invalid argument";
42711 case MA_INVALID_OPERATION: return "Invalid operation";
42712 case MA_OUT_OF_MEMORY: return "Out of memory";
42713 case MA_OUT_OF_RANGE: return "Out of range";
42714 case MA_ACCESS_DENIED: return "Permission denied";
42715 case MA_DOES_NOT_EXIST: return "Resource does not exist";
42716 case MA_ALREADY_EXISTS: return "Resource already exists";
42717 case MA_TOO_MANY_OPEN_FILES: return "Too many open files";
42718 case MA_INVALID_FILE: return "Invalid file";
42719 case MA_TOO_BIG: return "Too large";
42720 case MA_PATH_TOO_LONG: return "Path too long";
42721 case MA_NAME_TOO_LONG: return "Name too long";
42722 case MA_NOT_DIRECTORY: return "Not a directory";
42723 case MA_IS_DIRECTORY: return "Is a directory";
42724 case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty";
42725 case MA_END_OF_FILE: return "End of file";
42726 case MA_NO_SPACE: return "No space available";
42727 case MA_BUSY: return "Device or resource busy";
42728 case MA_IO_ERROR: return "Input/output error";
42729 case MA_INTERRUPT: return "Interrupted";
42730 case MA_UNAVAILABLE: return "Resource unavailable";
42731 case MA_ALREADY_IN_USE: return "Resource already in use";
42732 case MA_BAD_ADDRESS: return "Bad address";
42733 case MA_BAD_SEEK: return "Illegal seek";
42734 case MA_BAD_PIPE: return "Broken pipe";
42735 case MA_DEADLOCK: return "Deadlock";
42736 case MA_TOO_MANY_LINKS: return "Too many links";
42737 case MA_NOT_IMPLEMENTED: return "Not implemented";
42738 case MA_NO_MESSAGE: return "No message of desired type";
42739 case MA_BAD_MESSAGE: return "Invalid message";
42740 case MA_NO_DATA_AVAILABLE: return "No data available";
42741 case MA_INVALID_DATA: return "Invalid data";
42742 case MA_TIMEOUT: return "Timeout";
42743 case MA_NO_NETWORK: return "Network unavailable";
42744 case MA_NOT_UNIQUE: return "Not unique";
42745 case MA_NOT_SOCKET: return "Socket operation on non-socket";
42746 case MA_NO_ADDRESS: return "Destination address required";
42747 case MA_BAD_PROTOCOL: return "Protocol wrong type for socket";
42748 case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available";
42749 case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported";
42750 case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported";
42751 case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported";
42752 case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported";
42753 case MA_CONNECTION_RESET: return "Connection reset";
42754 case MA_ALREADY_CONNECTED: return "Already connected";
42755 case MA_NOT_CONNECTED: return "Not connected";
42756 case MA_CONNECTION_REFUSED: return "Connection refused";
42757 case MA_NO_HOST: return "No host";
42758 case MA_IN_PROGRESS: return "Operation in progress";
42759 case MA_CANCELLED: return "Operation cancelled";
42760 case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped";
42761 case MA_AT_END: return "Reached end of collection";
42763 case MA_FORMAT_NOT_SUPPORTED: return "Format not supported";
42764 case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported";
42765 case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported";
42766 case MA_NO_BACKEND: return "No backend";
42767 case MA_NO_DEVICE: return "No device";
42768 case MA_API_NOT_FOUND: return "API not found";
42769 case MA_INVALID_DEVICE_CONFIG: return "Invalid device config";
42771 case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized";
42772 case MA_DEVICE_NOT_STARTED: return "Device not started";
42774 case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend";
42775 case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device";
42776 case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device";
42777 case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device";
42779 default: return "Unknown error";
42783 MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
42785 if (pAllocationCallbacks != NULL) {
42786 return ma__malloc_from_callbacks(sz, pAllocationCallbacks);
42788 return ma__malloc_default(sz, NULL);
42792 MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
42794 if (pAllocationCallbacks != NULL) {
42795 if (pAllocationCallbacks->onRealloc != NULL) {
42796 return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData);
42798 return NULL; /* This requires a native implementation of realloc(). */
42801 return ma__realloc_default(p, sz, NULL);
42805 MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
42807 if (pAllocationCallbacks != NULL) {
42808 ma__free_from_callbacks(p, pAllocationCallbacks);
42810 ma__free_default(p, NULL);
42814 MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks)
42820 if (alignment == 0) {
42824 extraBytes = alignment-1 + sizeof(void*);
42826 pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks);
42827 if (pUnaligned == NULL) {
42831 pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1)));
42832 ((void**)pAligned)[-1] = pUnaligned;
42837 MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
42839 ma_free(((void**)p)[-1], pAllocationCallbacks);
42842 MA_API const char* ma_get_format_name(ma_format format)
42846 case ma_format_unknown: return "Unknown";
42847 case ma_format_u8: return "8-bit Unsigned Integer";
42848 case ma_format_s16: return "16-bit Signed Integer";
42849 case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)";
42850 case ma_format_s32: return "32-bit Signed Integer";
42851 case ma_format_f32: return "32-bit IEEE Floating Point";
42852 default: return "Invalid";
42856 MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels)
42859 for (i = 0; i < channels; ++i) {
42860 pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor);
42865 MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format)
42867 ma_uint32 sizes[] = {
42875 return sizes[format];
42880 MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead, ma_bool32 loop)
42882 ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource;
42885 if (pFramesRead != NULL) {
42889 if (pCallbacks == NULL) {
42890 return MA_INVALID_ARGS;
42893 if (pCallbacks->onRead == NULL) {
42894 return MA_NOT_IMPLEMENTED;
42897 /* A very small optimization for the non looping case. */
42898 if (loop == MA_FALSE) {
42899 return pCallbacks->onRead(pDataSource, pFramesOut, frameCount, pFramesRead);
42902 ma_uint32 channels;
42903 ma_uint32 sampleRate;
42904 if (ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate) != MA_SUCCESS) {
42905 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. */
42907 ma_result result = MA_SUCCESS;
42908 ma_uint64 totalFramesProcessed;
42909 void* pRunningFramesOut = pFramesOut;
42911 totalFramesProcessed = 0;
42912 while (totalFramesProcessed < frameCount) {
42913 ma_uint64 framesProcessed;
42914 ma_uint64 framesRemaining = frameCount - totalFramesProcessed;
42916 result = pCallbacks->onRead(pDataSource, pRunningFramesOut, framesRemaining, &framesProcessed);
42917 totalFramesProcessed += framesProcessed;
42920 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
42921 not necessarily considered an error.
42923 if (result != MA_SUCCESS && result != MA_AT_END) {
42928 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
42929 we've reached the end. To loop back to the start, all we need to do is seek back to the first frame.
42931 if (framesProcessed < framesRemaining || result == MA_AT_END) {
42932 if (ma_data_source_seek_to_pcm_frame(pDataSource, 0) != MA_SUCCESS) {
42937 if (pRunningFramesOut != NULL) {
42938 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels));
42942 if (pFramesRead != NULL) {
42943 *pFramesRead = totalFramesProcessed;
42951 MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked, ma_bool32 loop)
42953 return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked, loop);
42956 MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
42958 ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource;
42959 if (pCallbacks == NULL || pCallbacks->onSeek == NULL) {
42960 return MA_INVALID_ARGS;
42963 return pCallbacks->onSeek(pDataSource, frameIndex);
42966 MA_API ma_result ma_data_source_map(ma_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount)
42968 ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource;
42969 if (pCallbacks == NULL || pCallbacks->onMap == NULL) {
42970 return MA_INVALID_ARGS;
42973 return pCallbacks->onMap(pDataSource, ppFramesOut, pFrameCount);
42976 MA_API ma_result ma_data_source_unmap(ma_data_source* pDataSource, ma_uint64 frameCount)
42978 ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource;
42979 if (pCallbacks == NULL || pCallbacks->onUnmap == NULL) {
42980 return MA_INVALID_ARGS;
42983 return pCallbacks->onUnmap(pDataSource, frameCount);
42986 MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
42990 ma_uint32 channels;
42991 ma_uint32 sampleRate;
42992 ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource;
42994 if (pFormat != NULL) {
42995 *pFormat = ma_format_unknown;
42998 if (pChannels != NULL) {
43002 if (pSampleRate != NULL) {
43006 if (pCallbacks == NULL || pCallbacks->onGetDataFormat == NULL) {
43007 return MA_INVALID_ARGS;
43010 result = pCallbacks->onGetDataFormat(pDataSource, &format, &channels, &sampleRate);
43011 if (result != MA_SUCCESS) {
43015 if (pFormat != NULL) {
43018 if (pChannels != NULL) {
43019 *pChannels = channels;
43021 if (pSampleRate != NULL) {
43022 *pSampleRate = sampleRate;
43028 MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
43030 ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource;
43032 if (pCursor == NULL) {
43033 return MA_INVALID_ARGS;
43038 if (pCallbacks == NULL) {
43039 return MA_INVALID_ARGS;
43042 if (pCallbacks->onGetCursor == NULL) {
43043 return MA_NOT_IMPLEMENTED;
43046 return pCallbacks->onGetCursor(pDataSource, pCursor);
43049 MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
43051 ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource;
43053 if (pLength == NULL) {
43054 return MA_INVALID_ARGS;
43059 if (pCallbacks == NULL) {
43060 return MA_INVALID_ARGS;
43063 if (pCallbacks->onGetLength == NULL) {
43064 return MA_NOT_IMPLEMENTED;
43067 return pCallbacks->onGetLength(pDataSource, pLength);
43073 static ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
43075 ma_uint64 framesRead = ma_audio_buffer_ref_read_pcm_frames((ma_audio_buffer_ref*)pDataSource, pFramesOut, frameCount, MA_FALSE);
43077 if (pFramesRead != NULL) {
43078 *pFramesRead = framesRead;
43081 if (framesRead < frameCount) {
43088 static ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
43090 return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex);
43093 static ma_result ma_audio_buffer_ref__data_source_on_map(ma_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount)
43095 return ma_audio_buffer_ref_map((ma_audio_buffer_ref*)pDataSource, ppFramesOut, pFrameCount);
43098 static ma_result ma_audio_buffer_ref__data_source_on_unmap(ma_data_source* pDataSource, ma_uint64 frameCount)
43100 return ma_audio_buffer_ref_unmap((ma_audio_buffer_ref*)pDataSource, frameCount);
43103 static ma_result ma_audio_buffer_ref__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
43105 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
43107 *pFormat = pAudioBufferRef->format;
43108 *pChannels = pAudioBufferRef->channels;
43109 *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */
43114 static ma_result ma_audio_buffer_ref__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
43116 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
43118 *pCursor = pAudioBufferRef->cursor;
43123 static ma_result ma_audio_buffer_ref__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
43125 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
43127 *pLength = pAudioBufferRef->sizeInFrames;
43132 MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef)
43134 if (pAudioBufferRef == NULL) {
43135 return MA_INVALID_ARGS;
43138 MA_ZERO_OBJECT(pAudioBufferRef);
43140 pAudioBufferRef->ds.onRead = ma_audio_buffer_ref__data_source_on_read;
43141 pAudioBufferRef->ds.onSeek = ma_audio_buffer_ref__data_source_on_seek;
43142 pAudioBufferRef->ds.onMap = ma_audio_buffer_ref__data_source_on_map;
43143 pAudioBufferRef->ds.onUnmap = ma_audio_buffer_ref__data_source_on_unmap;
43144 pAudioBufferRef->ds.onGetDataFormat = ma_audio_buffer_ref__data_source_on_get_data_format;
43145 pAudioBufferRef->ds.onGetCursor = ma_audio_buffer_ref__data_source_on_get_cursor;
43146 pAudioBufferRef->ds.onGetLength = ma_audio_buffer_ref__data_source_on_get_length;
43147 pAudioBufferRef->format = format;
43148 pAudioBufferRef->channels = channels;
43149 pAudioBufferRef->cursor = 0;
43150 pAudioBufferRef->sizeInFrames = sizeInFrames;
43151 pAudioBufferRef->pData = pData;
43156 MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames)
43158 if (pAudioBufferRef == NULL) {
43159 return MA_INVALID_ARGS;
43162 pAudioBufferRef->cursor = 0;
43163 pAudioBufferRef->sizeInFrames = sizeInFrames;
43164 pAudioBufferRef->pData = pData;
43169 MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop)
43171 ma_uint64 totalFramesRead = 0;
43173 if (pAudioBufferRef == NULL) {
43177 if (frameCount == 0) {
43181 while (totalFramesRead < frameCount) {
43182 ma_uint64 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
43183 ma_uint64 framesRemaining = frameCount - totalFramesRead;
43184 ma_uint64 framesToRead;
43186 framesToRead = framesRemaining;
43187 if (framesToRead > framesAvailable) {
43188 framesToRead = framesAvailable;
43191 if (pFramesOut != NULL) {
43192 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);
43195 totalFramesRead += framesToRead;
43197 pAudioBufferRef->cursor += framesToRead;
43198 if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) {
43200 pAudioBufferRef->cursor = 0;
43202 break; /* We've reached the end and we're not looping. Done. */
43206 MA_ASSERT(pAudioBufferRef->cursor < pAudioBufferRef->sizeInFrames);
43209 return totalFramesRead;
43212 MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex)
43214 if (pAudioBufferRef == NULL) {
43215 return MA_INVALID_ARGS;
43218 if (frameIndex > pAudioBufferRef->sizeInFrames) {
43219 return MA_INVALID_ARGS;
43222 pAudioBufferRef->cursor = (size_t)frameIndex;
43227 MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount)
43229 ma_uint64 framesAvailable;
43230 ma_uint64 frameCount = 0;
43232 if (ppFramesOut != NULL) {
43233 *ppFramesOut = NULL; /* Safety. */
43236 if (pFrameCount != NULL) {
43237 frameCount = *pFrameCount;
43238 *pFrameCount = 0; /* Safety. */
43241 if (pAudioBufferRef == NULL || ppFramesOut == NULL || pFrameCount == NULL) {
43242 return MA_INVALID_ARGS;
43245 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
43246 if (frameCount > framesAvailable) {
43247 frameCount = framesAvailable;
43250 *ppFramesOut = ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels));
43251 *pFrameCount = frameCount;
43256 MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount)
43258 ma_uint64 framesAvailable;
43260 if (pAudioBufferRef == NULL) {
43261 return MA_INVALID_ARGS;
43264 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
43265 if (frameCount > framesAvailable) {
43266 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. */
43269 pAudioBufferRef->cursor += frameCount;
43271 if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) {
43272 return MA_AT_END; /* Successful. Need to tell the caller that the end has been reached so that it can loop if desired. */
43278 MA_API ma_result ma_audio_buffer_ref_at_end(ma_audio_buffer_ref* pAudioBufferRef)
43280 if (pAudioBufferRef == NULL) {
43284 return pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames;
43287 MA_API ma_result ma_audio_buffer_ref_get_available_frames(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames)
43289 if (pAvailableFrames == NULL) {
43290 return MA_INVALID_ARGS;
43293 *pAvailableFrames = 0;
43295 if (pAudioBufferRef == NULL) {
43296 return MA_INVALID_ARGS;
43299 if (pAudioBufferRef->sizeInFrames <= pAudioBufferRef->cursor) {
43300 *pAvailableFrames = 0;
43302 *pAvailableFrames = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
43311 MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks)
43313 ma_audio_buffer_config config;
43315 MA_ZERO_OBJECT(&config);
43316 config.format = format;
43317 config.channels = channels;
43318 config.sizeInFrames = sizeInFrames;
43319 config.pData = pData;
43320 ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks);
43325 static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer)
43329 if (pAudioBuffer == NULL) {
43330 return MA_INVALID_ARGS;
43333 MA_ZERO_MEMORY(pAudioBuffer, sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData)); /* Safety. Don't overwrite the extra data. */
43335 if (pConfig == NULL) {
43336 return MA_INVALID_ARGS;
43339 if (pConfig->sizeInFrames == 0) {
43340 return MA_INVALID_ARGS; /* Not allowing buffer sizes of 0 frames. */
43343 result = ma_audio_buffer_ref_init(pConfig->format, pConfig->channels, NULL, 0, &pAudioBuffer->ref);
43344 if (result != MA_SUCCESS) {
43348 ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks);
43351 ma_uint64 allocationSizeInBytes;
43354 allocationSizeInBytes = pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels);
43355 if (allocationSizeInBytes > MA_SIZE_MAX) {
43356 return MA_OUT_OF_MEMORY; /* Too big. */
43359 pData = ma__malloc_from_callbacks((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks); /* Safe cast to size_t. */
43360 if (pData == NULL) {
43361 return MA_OUT_OF_MEMORY;
43364 if (pConfig->pData != NULL) {
43365 ma_copy_pcm_frames(pData, pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
43367 ma_silence_pcm_frames(pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
43370 ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pData, pConfig->sizeInFrames);
43371 pAudioBuffer->ownsData = MA_TRUE;
43373 ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pConfig->pData, pConfig->sizeInFrames);
43374 pAudioBuffer->ownsData = MA_FALSE;
43380 static void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 doFree)
43382 if (pAudioBuffer == NULL) {
43386 if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) {
43387 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. */
43391 ma__free_from_callbacks(pAudioBuffer, &pAudioBuffer->allocationCallbacks);
43395 MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer)
43397 return ma_audio_buffer_init_ex(pConfig, MA_FALSE, pAudioBuffer);
43400 MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer)
43402 return ma_audio_buffer_init_ex(pConfig, MA_TRUE, pAudioBuffer);
43405 MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer)
43408 ma_audio_buffer* pAudioBuffer;
43409 ma_audio_buffer_config innerConfig; /* We'll be making some changes to the config, so need to make a copy. */
43410 ma_uint64 allocationSizeInBytes;
43412 if (ppAudioBuffer == NULL) {
43413 return MA_INVALID_ARGS;
43416 *ppAudioBuffer = NULL; /* Safety. */
43418 if (pConfig == NULL) {
43419 return MA_INVALID_ARGS;
43422 innerConfig = *pConfig;
43423 ma_allocation_callbacks_init_copy(&innerConfig.allocationCallbacks, &pConfig->allocationCallbacks);
43425 allocationSizeInBytes = sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData) + (pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels));
43426 if (allocationSizeInBytes > MA_SIZE_MAX) {
43427 return MA_OUT_OF_MEMORY; /* Too big. */
43430 pAudioBuffer = (ma_audio_buffer*)ma__malloc_from_callbacks((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks); /* Safe cast to size_t. */
43431 if (pAudioBuffer == NULL) {
43432 return MA_OUT_OF_MEMORY;
43435 if (pConfig->pData != NULL) {
43436 ma_copy_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
43438 ma_silence_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->sizeInFrames, pConfig->format, pConfig->channels);
43441 innerConfig.pData = &pAudioBuffer->_pExtraData[0];
43443 result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer);
43444 if (result != MA_SUCCESS) {
43445 ma__free_from_callbacks(pAudioBuffer, &innerConfig.allocationCallbacks);
43449 *ppAudioBuffer = pAudioBuffer;
43454 MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer)
43456 ma_audio_buffer_uninit_ex(pAudioBuffer, MA_FALSE);
43459 MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer)
43461 ma_audio_buffer_uninit_ex(pAudioBuffer, MA_TRUE);
43464 MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop)
43466 if (pAudioBuffer == NULL) {
43470 return ma_audio_buffer_ref_read_pcm_frames(&pAudioBuffer->ref, pFramesOut, frameCount, loop);
43473 MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex)
43475 if (pAudioBuffer == NULL) {
43476 return MA_INVALID_ARGS;
43479 return ma_audio_buffer_ref_seek_to_pcm_frame(&pAudioBuffer->ref, frameIndex);
43482 MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount)
43484 if (ppFramesOut != NULL) {
43485 *ppFramesOut = NULL; /* Safety. */
43488 if (pAudioBuffer == NULL) {
43489 if (pFrameCount != NULL) {
43493 return MA_INVALID_ARGS;
43496 return ma_audio_buffer_ref_map(&pAudioBuffer->ref, ppFramesOut, pFrameCount);
43499 MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount)
43501 if (pAudioBuffer == NULL) {
43502 return MA_INVALID_ARGS;
43505 return ma_audio_buffer_ref_unmap(&pAudioBuffer->ref, frameCount);
43508 MA_API ma_result ma_audio_buffer_at_end(ma_audio_buffer* pAudioBuffer)
43510 if (pAudioBuffer == NULL) {
43514 return ma_audio_buffer_ref_at_end(&pAudioBuffer->ref);
43517 MA_API ma_result ma_audio_buffer_get_available_frames(ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames)
43519 if (pAvailableFrames == NULL) {
43520 return MA_INVALID_ARGS;
43523 *pAvailableFrames = 0;
43525 if (pAudioBuffer == NULL) {
43526 return MA_INVALID_ARGS;
43529 return ma_audio_buffer_ref_get_available_frames(&pAudioBuffer->ref, pAvailableFrames);
43534 /**************************************************************************************************************************************************************
43538 **************************************************************************************************************************************************************/
43539 MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
43541 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
43543 if (pFile == NULL) {
43544 return MA_INVALID_ARGS;
43549 if (pVFS == NULL || pFilePath == NULL || openMode == 0) {
43550 return MA_INVALID_ARGS;
43553 if (pCallbacks->onOpen == NULL) {
43554 return MA_NOT_IMPLEMENTED;
43557 return pCallbacks->onOpen(pVFS, pFilePath, openMode, pFile);
43560 MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
43562 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
43564 if (pFile == NULL) {
43565 return MA_INVALID_ARGS;
43570 if (pVFS == NULL || pFilePath == NULL || openMode == 0) {
43571 return MA_INVALID_ARGS;
43574 if (pCallbacks->onOpenW == NULL) {
43575 return MA_NOT_IMPLEMENTED;
43578 return pCallbacks->onOpenW(pVFS, pFilePath, openMode, pFile);
43581 MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file)
43583 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
43585 if (pVFS == NULL || file == NULL) {
43586 return MA_INVALID_ARGS;
43589 if (pCallbacks->onClose == NULL) {
43590 return MA_NOT_IMPLEMENTED;
43593 return pCallbacks->onClose(pVFS, file);
43596 MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
43598 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
43600 if (pBytesRead != NULL) {
43604 if (pVFS == NULL || file == NULL || pDst == NULL) {
43605 return MA_INVALID_ARGS;
43608 if (pCallbacks->onRead == NULL) {
43609 return MA_NOT_IMPLEMENTED;
43612 return pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, pBytesRead);
43615 MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
43617 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
43619 if (pBytesWritten != NULL) {
43620 *pBytesWritten = 0;
43623 if (pVFS == NULL || file == NULL || pSrc == NULL) {
43624 return MA_INVALID_ARGS;
43627 if (pCallbacks->onWrite == NULL) {
43628 return MA_NOT_IMPLEMENTED;
43631 return pCallbacks->onWrite(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
43634 MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
43636 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
43638 if (pVFS == NULL || file == NULL) {
43639 return MA_INVALID_ARGS;
43642 if (pCallbacks->onSeek == NULL) {
43643 return MA_NOT_IMPLEMENTED;
43646 return pCallbacks->onSeek(pVFS, file, offset, origin);
43649 MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
43651 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
43653 if (pCursor == NULL) {
43654 return MA_INVALID_ARGS;
43659 if (pVFS == NULL || file == NULL) {
43660 return MA_INVALID_ARGS;
43663 if (pCallbacks->onTell == NULL) {
43664 return MA_NOT_IMPLEMENTED;
43667 return pCallbacks->onTell(pVFS, file, pCursor);
43670 MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
43672 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
43674 if (pInfo == NULL) {
43675 return MA_INVALID_ARGS;
43678 MA_ZERO_OBJECT(pInfo);
43680 if (pVFS == NULL || file == NULL) {
43681 return MA_INVALID_ARGS;
43684 if (pCallbacks->onInfo == NULL) {
43685 return MA_NOT_IMPLEMENTED;
43688 return pCallbacks->onInfo(pVFS, file, pInfo);
43692 static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks, ma_uint32 allocationType)
43700 (void)allocationType;
43702 if (ppData != NULL) {
43705 if (pSize != NULL) {
43709 if (ppData == NULL) {
43710 return MA_INVALID_ARGS;
43713 if (pFilePath != NULL) {
43714 result = ma_vfs_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
43716 result = ma_vfs_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file);
43718 if (result != MA_SUCCESS) {
43722 result = ma_vfs_info(pVFS, file, &info);
43723 if (result != MA_SUCCESS) {
43724 ma_vfs_close(pVFS, file);
43728 if (info.sizeInBytes > MA_SIZE_MAX) {
43729 ma_vfs_close(pVFS, file);
43733 pData = ma__malloc_from_callbacks((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */
43734 if (pData == NULL) {
43735 ma_vfs_close(pVFS, file);
43739 result = ma_vfs_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */
43740 ma_vfs_close(pVFS, file);
43742 if (result != MA_SUCCESS) {
43743 ma__free_from_callbacks(pData, pAllocationCallbacks);
43747 if (pSize != NULL) {
43748 *pSize = bytesRead;
43751 MA_ASSERT(ppData != NULL);
43757 MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks)
43759 return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks, 0 /*MA_ALLOCATION_TYPE_GENERAL*/);
43762 MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks)
43764 return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks, 0 /*MA_ALLOCATION_TYPE_GENERAL*/);
43768 #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP)
43769 static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition)
43771 *pDesiredAccess = 0;
43772 if ((openMode & MA_OPEN_MODE_READ) != 0) {
43773 *pDesiredAccess |= GENERIC_READ;
43775 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
43776 *pDesiredAccess |= GENERIC_WRITE;
43780 if ((openMode & MA_OPEN_MODE_READ) != 0) {
43781 *pShareMode |= FILE_SHARE_READ;
43784 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
43785 *pCreationDisposition = CREATE_ALWAYS; /* Opening in write mode. Truncate. */
43787 *pCreationDisposition = OPEN_EXISTING; /* Opening in read mode. File must exist. */
43791 static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
43794 DWORD dwDesiredAccess;
43796 DWORD dwCreationDisposition;
43800 ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition);
43802 hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
43803 if (hFile == INVALID_HANDLE_VALUE) {
43804 return ma_result_from_GetLastError(GetLastError());
43811 static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
43814 DWORD dwDesiredAccess;
43816 DWORD dwCreationDisposition;
43820 ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition);
43822 hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
43823 if (hFile == INVALID_HANDLE_VALUE) {
43824 return ma_result_from_GetLastError(GetLastError());
43831 static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file)
43835 if (CloseHandle((HANDLE)file) == 0) {
43836 return ma_result_from_GetLastError(GetLastError());
43843 static ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
43845 ma_result result = MA_SUCCESS;
43846 size_t totalBytesRead;
43850 totalBytesRead = 0;
43851 while (totalBytesRead < sizeInBytes) {
43852 size_t bytesRemaining;
43857 bytesRemaining = sizeInBytes - totalBytesRead;
43858 if (bytesRemaining >= 0xFFFFFFFF) {
43859 bytesToRead = 0xFFFFFFFF;
43861 bytesToRead = (DWORD)bytesRemaining;
43864 readResult = ReadFile((HANDLE)file, ma_offset_ptr(pDst, totalBytesRead), bytesToRead, &bytesRead, NULL);
43865 if (readResult == 1 && bytesRead == 0) {
43869 totalBytesRead += bytesRead;
43871 if (bytesRead < bytesToRead) {
43875 if (readResult == 0) {
43876 result = ma_result_from_GetLastError(GetLastError());
43881 if (pBytesRead != NULL) {
43882 *pBytesRead = totalBytesRead;
43888 static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
43890 ma_result result = MA_SUCCESS;
43891 size_t totalBytesWritten;
43895 totalBytesWritten = 0;
43896 while (totalBytesWritten < sizeInBytes) {
43897 size_t bytesRemaining;
43898 DWORD bytesToWrite;
43899 DWORD bytesWritten;
43902 bytesRemaining = sizeInBytes - totalBytesWritten;
43903 if (bytesRemaining >= 0xFFFFFFFF) {
43904 bytesToWrite = 0xFFFFFFFF;
43906 bytesToWrite = (DWORD)bytesRemaining;
43909 writeResult = WriteFile((HANDLE)file, ma_offset_ptr(pSrc, totalBytesWritten), bytesToWrite, &bytesWritten, NULL);
43910 totalBytesWritten += bytesWritten;
43912 if (writeResult == 0) {
43913 result = ma_result_from_GetLastError(GetLastError());
43918 if (pBytesWritten != NULL) {
43919 *pBytesWritten = totalBytesWritten;
43926 static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
43928 LARGE_INTEGER liDistanceToMove;
43929 DWORD dwMoveMethod;
43934 liDistanceToMove.QuadPart = offset;
43936 /* */ if (origin == ma_seek_origin_current) {
43937 dwMoveMethod = FILE_CURRENT;
43938 } else if (origin == ma_seek_origin_end) {
43939 dwMoveMethod = FILE_END;
43941 dwMoveMethod = FILE_BEGIN;
43944 #if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__)
43945 /* No SetFilePointerEx() so restrict to 31 bits. */
43946 if (origin > 0x7FFFFFFF) {
43947 return MA_OUT_OF_RANGE;
43950 result = SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod);
43952 result = SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod);
43955 return ma_result_from_GetLastError(GetLastError());
43961 static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
43963 LARGE_INTEGER liZero;
43964 LARGE_INTEGER liTell;
43966 #if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__)
43972 liZero.QuadPart = 0;
43974 #if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__)
43975 result = SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT);
43976 liTell.QuadPart = tell;
43978 result = SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT);
43981 return ma_result_from_GetLastError(GetLastError());
43984 if (pCursor != NULL) {
43985 *pCursor = liTell.QuadPart;
43991 static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
43993 BY_HANDLE_FILE_INFORMATION fi;
43998 result = GetFileInformationByHandle((HANDLE)file, &fi);
44000 return ma_result_from_GetLastError(GetLastError());
44003 pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow);
44008 static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
44012 const char* pOpenModeStr;
44014 MA_ASSERT(pFilePath != NULL);
44015 MA_ASSERT(openMode != 0);
44016 MA_ASSERT(pFile != NULL);
44020 if ((openMode & MA_OPEN_MODE_READ) != 0) {
44021 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
44022 pOpenModeStr = "r+";
44024 pOpenModeStr = "rb";
44027 pOpenModeStr = "wb";
44030 result = ma_fopen(&pFileStd, pFilePath, pOpenModeStr);
44031 if (result != MA_SUCCESS) {
44040 static ma_result ma_default_vfs_open_w__stdio(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
44044 const wchar_t* pOpenModeStr;
44046 MA_ASSERT(pFilePath != NULL);
44047 MA_ASSERT(openMode != 0);
44048 MA_ASSERT(pFile != NULL);
44052 if ((openMode & MA_OPEN_MODE_READ) != 0) {
44053 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
44054 pOpenModeStr = L"r+";
44056 pOpenModeStr = L"rb";
44059 pOpenModeStr = L"wb";
44062 result = ma_wfopen(&pFileStd, pFilePath, pOpenModeStr, (pVFS != NULL) ? &((ma_default_vfs*)pVFS)->allocationCallbacks : NULL);
44063 if (result != MA_SUCCESS) {
44072 static ma_result ma_default_vfs_close__stdio(ma_vfs* pVFS, ma_vfs_file file)
44074 MA_ASSERT(file != NULL);
44078 fclose((FILE*)file);
44083 static ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
44087 MA_ASSERT(file != NULL);
44088 MA_ASSERT(pDst != NULL);
44092 result = fread(pDst, 1, sizeInBytes, (FILE*)file);
44094 if (pBytesRead != NULL) {
44095 *pBytesRead = result;
44098 if (result != sizeInBytes) {
44099 if (feof((FILE*)file)) {
44100 return MA_END_OF_FILE;
44102 return ma_result_from_errno(ferror((FILE*)file));
44109 static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
44113 MA_ASSERT(file != NULL);
44114 MA_ASSERT(pSrc != NULL);
44118 result = fwrite(pSrc, 1, sizeInBytes, (FILE*)file);
44120 if (pBytesWritten != NULL) {
44121 *pBytesWritten = result;
44124 if (result != sizeInBytes) {
44125 return ma_result_from_errno(ferror((FILE*)file));
44131 static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
44135 MA_ASSERT(file != NULL);
44139 #if defined(_WIN32)
44140 #if defined(_MSC_VER) && _MSC_VER > 1200
44141 result = _fseeki64((FILE*)file, offset, origin);
44143 /* No _fseeki64() so restrict to 31 bits. */
44144 if (origin > 0x7FFFFFFF) {
44145 return MA_OUT_OF_RANGE;
44148 result = fseek((FILE*)file, (int)offset, origin);
44151 result = fseek((FILE*)file, (long int)offset, origin);
44160 static ma_result ma_default_vfs_tell__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
44164 MA_ASSERT(file != NULL);
44165 MA_ASSERT(pCursor != NULL);
44169 #if defined(_WIN32)
44170 #if defined(_MSC_VER) && _MSC_VER > 1200
44171 result = _ftelli64((FILE*)file);
44173 result = ftell((FILE*)file);
44176 result = ftell((FILE*)file);
44184 #if !defined(_MSC_VER) && !((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)) && !defined(MA_BSD)
44185 int fileno(FILE *stream);
44188 static ma_result ma_default_vfs_info__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
44193 MA_ASSERT(file != NULL);
44194 MA_ASSERT(pInfo != NULL);
44198 #if defined(_MSC_VER)
44199 fd = _fileno((FILE*)file);
44201 fd = fileno((FILE*)file);
44204 if (fstat(fd, &info) != 0) {
44205 return ma_result_from_errno(errno);
44208 pInfo->sizeInBytes = info.st_size;
44215 static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
44217 if (pFile == NULL) {
44218 return MA_INVALID_ARGS;
44223 if (pFilePath == NULL || openMode == 0) {
44224 return MA_INVALID_ARGS;
44227 #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP)
44228 return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile);
44230 return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile);
44234 static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
44236 if (pFile == NULL) {
44237 return MA_INVALID_ARGS;
44242 if (pFilePath == NULL || openMode == 0) {
44243 return MA_INVALID_ARGS;
44246 #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP)
44247 return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile);
44249 return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile);
44253 static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file)
44255 if (file == NULL) {
44256 return MA_INVALID_ARGS;
44259 #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP)
44260 return ma_default_vfs_close__win32(pVFS, file);
44262 return ma_default_vfs_close__stdio(pVFS, file);
44266 static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
44268 if (pBytesRead != NULL) {
44272 if (file == NULL || pDst == NULL) {
44273 return MA_INVALID_ARGS;
44276 #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP)
44277 return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead);
44279 return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead);
44283 static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
44285 if (pBytesWritten != NULL) {
44286 *pBytesWritten = 0;
44289 if (file == NULL || pSrc == NULL) {
44290 return MA_INVALID_ARGS;
44293 #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP)
44294 return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
44296 return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
44300 static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
44302 if (file == NULL) {
44303 return MA_INVALID_ARGS;
44306 #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP)
44307 return ma_default_vfs_seek__win32(pVFS, file, offset, origin);
44309 return ma_default_vfs_seek__stdio(pVFS, file, offset, origin);
44313 static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
44315 if (pCursor == NULL) {
44316 return MA_INVALID_ARGS;
44321 if (file == NULL) {
44322 return MA_INVALID_ARGS;
44325 #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP)
44326 return ma_default_vfs_tell__win32(pVFS, file, pCursor);
44328 return ma_default_vfs_tell__stdio(pVFS, file, pCursor);
44332 static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
44334 if (pInfo == NULL) {
44335 return MA_INVALID_ARGS;
44338 MA_ZERO_OBJECT(pInfo);
44340 if (file == NULL) {
44341 return MA_INVALID_ARGS;
44344 #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP)
44345 return ma_default_vfs_info__win32(pVFS, file, pInfo);
44347 return ma_default_vfs_info__stdio(pVFS, file, pInfo);
44352 MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks)
44354 if (pVFS == NULL) {
44355 return MA_INVALID_ARGS;
44358 pVFS->cb.onOpen = ma_default_vfs_open;
44359 pVFS->cb.onOpenW = ma_default_vfs_open_w;
44360 pVFS->cb.onClose = ma_default_vfs_close;
44361 pVFS->cb.onRead = ma_default_vfs_read;
44362 pVFS->cb.onWrite = ma_default_vfs_write;
44363 pVFS->cb.onSeek = ma_default_vfs_seek;
44364 pVFS->cb.onTell = ma_default_vfs_tell;
44365 pVFS->cb.onInfo = ma_default_vfs_info;
44366 ma_allocation_callbacks_init_copy(&pVFS->allocationCallbacks, pAllocationCallbacks);
44372 MA_API ma_result ma_vfs_or_default_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
44374 if (pVFS != NULL) {
44375 return ma_vfs_open(pVFS, pFilePath, openMode, pFile);
44377 return ma_default_vfs_open(pVFS, pFilePath, openMode, pFile);
44381 MA_API ma_result ma_vfs_or_default_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
44383 if (pVFS != NULL) {
44384 return ma_vfs_open_w(pVFS, pFilePath, openMode, pFile);
44386 return ma_default_vfs_open_w(pVFS, pFilePath, openMode, pFile);
44390 MA_API ma_result ma_vfs_or_default_close(ma_vfs* pVFS, ma_vfs_file file)
44392 if (pVFS != NULL) {
44393 return ma_vfs_close(pVFS, file);
44395 return ma_default_vfs_close(pVFS, file);
44399 MA_API ma_result ma_vfs_or_default_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
44401 if (pVFS != NULL) {
44402 return ma_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead);
44404 return ma_default_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead);
44408 MA_API ma_result ma_vfs_or_default_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
44410 if (pVFS != NULL) {
44411 return ma_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
44413 return ma_default_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
44417 MA_API ma_result ma_vfs_or_default_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
44419 if (pVFS != NULL) {
44420 return ma_vfs_seek(pVFS, file, offset, origin);
44422 return ma_default_vfs_seek(pVFS, file, offset, origin);
44426 MA_API ma_result ma_vfs_or_default_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
44428 if (pVFS != NULL) {
44429 return ma_vfs_tell(pVFS, file, pCursor);
44431 return ma_default_vfs_tell(pVFS, file, pCursor);
44435 MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
44437 if (pVFS != NULL) {
44438 return ma_vfs_info(pVFS, file, pInfo);
44440 return ma_default_vfs_info(pVFS, file, pInfo);
44446 /**************************************************************************************************************************************************************
44448 Decoding and Encoding Headers. These are auto-generated from a tool.
44450 **************************************************************************************************************************************************************/
44451 #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))
44452 /* dr_wav_h begin */
44458 #define DRWAV_STRINGIFY(x) #x
44459 #define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x)
44460 #define DRWAV_VERSION_MAJOR 0
44461 #define DRWAV_VERSION_MINOR 12
44462 #define DRWAV_VERSION_REVISION 19
44463 #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)
44464 #include <stddef.h>
44465 typedef signed char drwav_int8;
44466 typedef unsigned char drwav_uint8;
44467 typedef signed short drwav_int16;
44468 typedef unsigned short drwav_uint16;
44469 typedef signed int drwav_int32;
44470 typedef unsigned int drwav_uint32;
44471 #if defined(_MSC_VER)
44472 typedef signed __int64 drwav_int64;
44473 typedef unsigned __int64 drwav_uint64;
44475 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
44476 #pragma GCC diagnostic push
44477 #pragma GCC diagnostic ignored "-Wlong-long"
44478 #if defined(__clang__)
44479 #pragma GCC diagnostic ignored "-Wc++11-long-long"
44482 typedef signed long long drwav_int64;
44483 typedef unsigned long long drwav_uint64;
44484 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
44485 #pragma GCC diagnostic pop
44488 #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
44489 typedef drwav_uint64 drwav_uintptr;
44491 typedef drwav_uint32 drwav_uintptr;
44493 typedef drwav_uint8 drwav_bool8;
44494 typedef drwav_uint32 drwav_bool32;
44495 #define DRWAV_TRUE 1
44496 #define DRWAV_FALSE 0
44497 #if !defined(DRWAV_API)
44498 #if defined(DRWAV_DLL)
44499 #if defined(_WIN32)
44500 #define DRWAV_DLL_IMPORT __declspec(dllimport)
44501 #define DRWAV_DLL_EXPORT __declspec(dllexport)
44502 #define DRWAV_DLL_PRIVATE static
44504 #if defined(__GNUC__) && __GNUC__ >= 4
44505 #define DRWAV_DLL_IMPORT __attribute__((visibility("default")))
44506 #define DRWAV_DLL_EXPORT __attribute__((visibility("default")))
44507 #define DRWAV_DLL_PRIVATE __attribute__((visibility("hidden")))
44509 #define DRWAV_DLL_IMPORT
44510 #define DRWAV_DLL_EXPORT
44511 #define DRWAV_DLL_PRIVATE static
44514 #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION)
44515 #define DRWAV_API DRWAV_DLL_EXPORT
44517 #define DRWAV_API DRWAV_DLL_IMPORT
44519 #define DRWAV_PRIVATE DRWAV_DLL_PRIVATE
44521 #define DRWAV_API extern
44522 #define DRWAV_PRIVATE static
44525 typedef drwav_int32 drwav_result;
44526 #define DRWAV_SUCCESS 0
44527 #define DRWAV_ERROR -1
44528 #define DRWAV_INVALID_ARGS -2
44529 #define DRWAV_INVALID_OPERATION -3
44530 #define DRWAV_OUT_OF_MEMORY -4
44531 #define DRWAV_OUT_OF_RANGE -5
44532 #define DRWAV_ACCESS_DENIED -6
44533 #define DRWAV_DOES_NOT_EXIST -7
44534 #define DRWAV_ALREADY_EXISTS -8
44535 #define DRWAV_TOO_MANY_OPEN_FILES -9
44536 #define DRWAV_INVALID_FILE -10
44537 #define DRWAV_TOO_BIG -11
44538 #define DRWAV_PATH_TOO_LONG -12
44539 #define DRWAV_NAME_TOO_LONG -13
44540 #define DRWAV_NOT_DIRECTORY -14
44541 #define DRWAV_IS_DIRECTORY -15
44542 #define DRWAV_DIRECTORY_NOT_EMPTY -16
44543 #define DRWAV_END_OF_FILE -17
44544 #define DRWAV_NO_SPACE -18
44545 #define DRWAV_BUSY -19
44546 #define DRWAV_IO_ERROR -20
44547 #define DRWAV_INTERRUPT -21
44548 #define DRWAV_UNAVAILABLE -22
44549 #define DRWAV_ALREADY_IN_USE -23
44550 #define DRWAV_BAD_ADDRESS -24
44551 #define DRWAV_BAD_SEEK -25
44552 #define DRWAV_BAD_PIPE -26
44553 #define DRWAV_DEADLOCK -27
44554 #define DRWAV_TOO_MANY_LINKS -28
44555 #define DRWAV_NOT_IMPLEMENTED -29
44556 #define DRWAV_NO_MESSAGE -30
44557 #define DRWAV_BAD_MESSAGE -31
44558 #define DRWAV_NO_DATA_AVAILABLE -32
44559 #define DRWAV_INVALID_DATA -33
44560 #define DRWAV_TIMEOUT -34
44561 #define DRWAV_NO_NETWORK -35
44562 #define DRWAV_NOT_UNIQUE -36
44563 #define DRWAV_NOT_SOCKET -37
44564 #define DRWAV_NO_ADDRESS -38
44565 #define DRWAV_BAD_PROTOCOL -39
44566 #define DRWAV_PROTOCOL_UNAVAILABLE -40
44567 #define DRWAV_PROTOCOL_NOT_SUPPORTED -41
44568 #define DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED -42
44569 #define DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED -43
44570 #define DRWAV_SOCKET_NOT_SUPPORTED -44
44571 #define DRWAV_CONNECTION_RESET -45
44572 #define DRWAV_ALREADY_CONNECTED -46
44573 #define DRWAV_NOT_CONNECTED -47
44574 #define DRWAV_CONNECTION_REFUSED -48
44575 #define DRWAV_NO_HOST -49
44576 #define DRWAV_IN_PROGRESS -50
44577 #define DRWAV_CANCELLED -51
44578 #define DRWAV_MEMORY_ALREADY_MAPPED -52
44579 #define DRWAV_AT_END -53
44580 #define DR_WAVE_FORMAT_PCM 0x1
44581 #define DR_WAVE_FORMAT_ADPCM 0x2
44582 #define DR_WAVE_FORMAT_IEEE_FLOAT 0x3
44583 #define DR_WAVE_FORMAT_ALAW 0x6
44584 #define DR_WAVE_FORMAT_MULAW 0x7
44585 #define DR_WAVE_FORMAT_DVI_ADPCM 0x11
44586 #define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE
44587 #ifndef DRWAV_MAX_SMPL_LOOPS
44588 #define DRWAV_MAX_SMPL_LOOPS 1
44590 #define DRWAV_SEQUENTIAL 0x00000001
44591 DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision);
44592 DRWAV_API const char* drwav_version_string(void);
44595 drwav_seek_origin_start,
44596 drwav_seek_origin_current
44597 } drwav_seek_origin;
44600 drwav_container_riff,
44601 drwav_container_w64,
44602 drwav_container_rf64
44608 drwav_uint8 fourcc[4];
44609 drwav_uint8 guid[16];
44611 drwav_uint64 sizeInBytes;
44612 unsigned int paddingSize;
44613 } drwav_chunk_header;
44616 drwav_uint16 formatTag;
44617 drwav_uint16 channels;
44618 drwav_uint32 sampleRate;
44619 drwav_uint32 avgBytesPerSec;
44620 drwav_uint16 blockAlign;
44621 drwav_uint16 bitsPerSample;
44622 drwav_uint16 extendedSize;
44623 drwav_uint16 validBitsPerSample;
44624 drwav_uint32 channelMask;
44625 drwav_uint8 subFormat[16];
44627 DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT);
44628 typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
44629 typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite);
44630 typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin);
44631 typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader, drwav_container container, const drwav_fmt* pFMT);
44635 void* (* onMalloc)(size_t sz, void* pUserData);
44636 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
44637 void (* onFree)(void* p, void* pUserData);
44638 } drwav_allocation_callbacks;
44641 const drwav_uint8* data;
44643 size_t currentReadPos;
44644 } drwav__memory_stream;
44650 size_t dataCapacity;
44651 size_t currentWritePos;
44652 } drwav__memory_stream_write;
44655 drwav_container container;
44656 drwav_uint32 format;
44657 drwav_uint32 channels;
44658 drwav_uint32 sampleRate;
44659 drwav_uint32 bitsPerSample;
44660 } drwav_data_format;
44663 drwav_uint32 cuePointId;
44665 drwav_uint32 start;
44667 drwav_uint32 fraction;
44668 drwav_uint32 playCount;
44672 drwav_uint32 manufacturer;
44673 drwav_uint32 product;
44674 drwav_uint32 samplePeriod;
44675 drwav_uint32 midiUnityNotes;
44676 drwav_uint32 midiPitchFraction;
44677 drwav_uint32 smpteFormat;
44678 drwav_uint32 smpteOffset;
44679 drwav_uint32 numSampleLoops;
44680 drwav_uint32 samplerData;
44681 drwav_smpl_loop loops[DRWAV_MAX_SMPL_LOOPS];
44685 drwav_read_proc onRead;
44686 drwav_write_proc onWrite;
44687 drwav_seek_proc onSeek;
44689 drwav_allocation_callbacks allocationCallbacks;
44690 drwav_container container;
44692 drwav_uint32 sampleRate;
44693 drwav_uint16 channels;
44694 drwav_uint16 bitsPerSample;
44695 drwav_uint16 translatedFormatTag;
44696 drwav_uint64 totalPCMFrameCount;
44697 drwav_uint64 dataChunkDataSize;
44698 drwav_uint64 dataChunkDataPos;
44699 drwav_uint64 bytesRemaining;
44700 drwav_uint64 dataChunkDataSizeTargetWrite;
44701 drwav_bool32 isSequentialWrite;
44703 drwav__memory_stream memoryStream;
44704 drwav__memory_stream_write memoryStreamWrite;
44707 drwav_uint64 iCurrentPCMFrame;
44711 drwav_uint32 bytesRemainingInBlock;
44712 drwav_uint16 predictor[2];
44713 drwav_int32 delta[2];
44714 drwav_int32 cachedFrames[4];
44715 drwav_uint32 cachedFrameCount;
44716 drwav_int32 prevFrames[2][2];
44720 drwav_uint32 bytesRemainingInBlock;
44721 drwav_int32 predictor[2];
44722 drwav_int32 stepIndex[2];
44723 drwav_int32 cachedFrames[16];
44724 drwav_uint32 cachedFrameCount;
44727 DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
44728 DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
44729 DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
44730 DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
44731 DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
44732 DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
44733 DRWAV_API drwav_result drwav_uninit(drwav* pWav);
44734 DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut);
44735 DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
44736 DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
44737 DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
44738 DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex);
44739 DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData);
44740 DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
44741 DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
44742 DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
44743 #ifndef DR_WAV_NO_CONVERSION_API
44744 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
44745 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
44746 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
44747 DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
44748 DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
44749 DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount);
44750 DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount);
44751 DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount);
44752 DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
44753 DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
44754 DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
44755 DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
44756 DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
44757 DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
44758 DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount);
44759 DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
44760 DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount);
44761 DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount);
44762 DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
44763 DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
44764 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
44765 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
44766 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
44767 DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
44768 DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount);
44769 DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
44770 DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount);
44771 DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount);
44772 DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
44773 DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
44775 #ifndef DR_WAV_NO_STDIO
44776 DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks);
44777 DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
44778 DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks);
44779 DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
44780 DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
44781 DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
44782 DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
44783 DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
44784 DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
44785 DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
44787 DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks);
44788 DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
44789 DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
44790 DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
44791 DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
44792 #ifndef DR_WAV_NO_CONVERSION_API
44793 DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
44794 DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
44795 DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
44796 #ifndef DR_WAV_NO_STDIO
44797 DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
44798 DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
44799 DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
44800 DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
44801 DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
44802 DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
44804 DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
44805 DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
44806 DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
44808 DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks);
44809 DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data);
44810 DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data);
44811 DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data);
44812 DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data);
44813 DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data);
44814 DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data);
44815 DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]);
44816 DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b);
44822 #endif /* MA_NO_WAV */
44824 #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)
44825 /* dr_flac_h begin */
44831 #define DRFLAC_STRINGIFY(x) #x
44832 #define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x)
44833 #define DRFLAC_VERSION_MAJOR 0
44834 #define DRFLAC_VERSION_MINOR 12
44835 #define DRFLAC_VERSION_REVISION 29
44836 #define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION)
44837 #include <stddef.h>
44838 typedef signed char drflac_int8;
44839 typedef unsigned char drflac_uint8;
44840 typedef signed short drflac_int16;
44841 typedef unsigned short drflac_uint16;
44842 typedef signed int drflac_int32;
44843 typedef unsigned int drflac_uint32;
44844 #if defined(_MSC_VER)
44845 typedef signed __int64 drflac_int64;
44846 typedef unsigned __int64 drflac_uint64;
44848 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
44849 #pragma GCC diagnostic push
44850 #pragma GCC diagnostic ignored "-Wlong-long"
44851 #if defined(__clang__)
44852 #pragma GCC diagnostic ignored "-Wc++11-long-long"
44855 typedef signed long long drflac_int64;
44856 typedef unsigned long long drflac_uint64;
44857 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
44858 #pragma GCC diagnostic pop
44861 #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
44862 typedef drflac_uint64 drflac_uintptr;
44864 typedef drflac_uint32 drflac_uintptr;
44866 typedef drflac_uint8 drflac_bool8;
44867 typedef drflac_uint32 drflac_bool32;
44868 #define DRFLAC_TRUE 1
44869 #define DRFLAC_FALSE 0
44870 #if !defined(DRFLAC_API)
44871 #if defined(DRFLAC_DLL)
44872 #if defined(_WIN32)
44873 #define DRFLAC_DLL_IMPORT __declspec(dllimport)
44874 #define DRFLAC_DLL_EXPORT __declspec(dllexport)
44875 #define DRFLAC_DLL_PRIVATE static
44877 #if defined(__GNUC__) && __GNUC__ >= 4
44878 #define DRFLAC_DLL_IMPORT __attribute__((visibility("default")))
44879 #define DRFLAC_DLL_EXPORT __attribute__((visibility("default")))
44880 #define DRFLAC_DLL_PRIVATE __attribute__((visibility("hidden")))
44882 #define DRFLAC_DLL_IMPORT
44883 #define DRFLAC_DLL_EXPORT
44884 #define DRFLAC_DLL_PRIVATE static
44887 #if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION)
44888 #define DRFLAC_API DRFLAC_DLL_EXPORT
44890 #define DRFLAC_API DRFLAC_DLL_IMPORT
44892 #define DRFLAC_PRIVATE DRFLAC_DLL_PRIVATE
44894 #define DRFLAC_API extern
44895 #define DRFLAC_PRIVATE static
44898 #if defined(_MSC_VER) && _MSC_VER >= 1700
44899 #define DRFLAC_DEPRECATED __declspec(deprecated)
44900 #elif (defined(__GNUC__) && __GNUC__ >= 4)
44901 #define DRFLAC_DEPRECATED __attribute__((deprecated))
44902 #elif defined(__has_feature)
44903 #if __has_feature(attribute_deprecated)
44904 #define DRFLAC_DEPRECATED __attribute__((deprecated))
44906 #define DRFLAC_DEPRECATED
44909 #define DRFLAC_DEPRECATED
44911 DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision);
44912 DRFLAC_API const char* drflac_version_string(void);
44913 #ifndef DR_FLAC_BUFFER_SIZE
44914 #define DR_FLAC_BUFFER_SIZE 4096
44916 #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
44917 #define DRFLAC_64BIT
44919 #ifdef DRFLAC_64BIT
44920 typedef drflac_uint64 drflac_cache_t;
44922 typedef drflac_uint32 drflac_cache_t;
44924 #define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0
44925 #define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1
44926 #define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2
44927 #define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3
44928 #define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4
44929 #define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5
44930 #define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6
44931 #define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127
44932 #define DRFLAC_PICTURE_TYPE_OTHER 0
44933 #define DRFLAC_PICTURE_TYPE_FILE_ICON 1
44934 #define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2
44935 #define DRFLAC_PICTURE_TYPE_COVER_FRONT 3
44936 #define DRFLAC_PICTURE_TYPE_COVER_BACK 4
44937 #define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5
44938 #define DRFLAC_PICTURE_TYPE_MEDIA 6
44939 #define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7
44940 #define DRFLAC_PICTURE_TYPE_ARTIST 8
44941 #define DRFLAC_PICTURE_TYPE_CONDUCTOR 9
44942 #define DRFLAC_PICTURE_TYPE_BAND 10
44943 #define DRFLAC_PICTURE_TYPE_COMPOSER 11
44944 #define DRFLAC_PICTURE_TYPE_LYRICIST 12
44945 #define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13
44946 #define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14
44947 #define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15
44948 #define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16
44949 #define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17
44950 #define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18
44951 #define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19
44952 #define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20
44955 drflac_container_native,
44956 drflac_container_ogg,
44957 drflac_container_unknown
44958 } drflac_container;
44961 drflac_seek_origin_start,
44962 drflac_seek_origin_current
44963 } drflac_seek_origin;
44967 drflac_uint64 firstPCMFrame;
44968 drflac_uint64 flacFrameOffset;
44969 drflac_uint16 pcmFrameCount;
44970 } drflac_seekpoint;
44974 drflac_uint16 minBlockSizeInPCMFrames;
44975 drflac_uint16 maxBlockSizeInPCMFrames;
44976 drflac_uint32 minFrameSizeInPCMFrames;
44977 drflac_uint32 maxFrameSizeInPCMFrames;
44978 drflac_uint32 sampleRate;
44979 drflac_uint8 channels;
44980 drflac_uint8 bitsPerSample;
44981 drflac_uint64 totalPCMFrameCount;
44982 drflac_uint8 md5[16];
44983 } drflac_streaminfo;
44986 drflac_uint32 type;
44987 const void* pRawData;
44988 drflac_uint32 rawDataSize;
44991 drflac_streaminfo streaminfo;
45000 drflac_uint32 dataSize;
45004 drflac_uint32 seekpointCount;
45005 const drflac_seekpoint* pSeekpoints;
45009 drflac_uint32 vendorLength;
45010 const char* vendor;
45011 drflac_uint32 commentCount;
45012 const void* pComments;
45017 drflac_uint64 leadInSampleCount;
45018 drflac_bool32 isCD;
45019 drflac_uint8 trackCount;
45020 const void* pTrackData;
45024 drflac_uint32 type;
45025 drflac_uint32 mimeLength;
45027 drflac_uint32 descriptionLength;
45028 const char* description;
45029 drflac_uint32 width;
45030 drflac_uint32 height;
45031 drflac_uint32 colorDepth;
45032 drflac_uint32 indexColorCount;
45033 drflac_uint32 pictureDataSize;
45034 const drflac_uint8* pPictureData;
45038 typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
45039 typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin);
45040 typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata);
45044 void* (* onMalloc)(size_t sz, void* pUserData);
45045 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
45046 void (* onFree)(void* p, void* pUserData);
45047 } drflac_allocation_callbacks;
45050 const drflac_uint8* data;
45052 size_t currentReadPos;
45053 } drflac__memory_stream;
45056 drflac_read_proc onRead;
45057 drflac_seek_proc onSeek;
45059 size_t unalignedByteCount;
45060 drflac_cache_t unalignedCache;
45061 drflac_uint32 nextL2Line;
45062 drflac_uint32 consumedBits;
45063 drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)];
45064 drflac_cache_t cache;
45065 drflac_uint16 crc16;
45066 drflac_cache_t crc16Cache;
45067 drflac_uint32 crc16CacheIgnoredBytes;
45071 drflac_uint8 subframeType;
45072 drflac_uint8 wastedBitsPerSample;
45073 drflac_uint8 lpcOrder;
45074 drflac_int32* pSamplesS32;
45078 drflac_uint64 pcmFrameNumber;
45079 drflac_uint32 flacFrameNumber;
45080 drflac_uint32 sampleRate;
45081 drflac_uint16 blockSizeInPCMFrames;
45082 drflac_uint8 channelAssignment;
45083 drflac_uint8 bitsPerSample;
45085 } drflac_frame_header;
45088 drflac_frame_header header;
45089 drflac_uint32 pcmFramesRemaining;
45090 drflac_subframe subframes[8];
45094 drflac_meta_proc onMeta;
45096 drflac_allocation_callbacks allocationCallbacks;
45097 drflac_uint32 sampleRate;
45098 drflac_uint8 channels;
45099 drflac_uint8 bitsPerSample;
45100 drflac_uint16 maxBlockSizeInPCMFrames;
45101 drflac_uint64 totalPCMFrameCount;
45102 drflac_container container;
45103 drflac_uint32 seekpointCount;
45104 drflac_frame currentFLACFrame;
45105 drflac_uint64 currentPCMFrame;
45106 drflac_uint64 firstFLACFramePosInBytes;
45107 drflac__memory_stream memoryStream;
45108 drflac_int32* pDecodedSamples;
45109 drflac_seekpoint* pSeekpoints;
45111 drflac_bool32 _noSeekTableSeek : 1;
45112 drflac_bool32 _noBinarySearchSeek : 1;
45113 drflac_bool32 _noBruteForceSeek : 1;
45115 drflac_uint8 pExtraData[1];
45117 DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
45118 DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
45119 DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
45120 DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
45121 DRFLAC_API void drflac_close(drflac* pFlac);
45122 DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut);
45123 DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut);
45124 DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut);
45125 DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex);
45126 #ifndef DR_FLAC_NO_STDIO
45127 DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks);
45128 DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks);
45129 DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
45130 DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
45132 DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks);
45133 DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
45134 DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
45135 DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
45136 DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
45137 #ifndef DR_FLAC_NO_STDIO
45138 DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
45139 DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
45140 DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
45142 DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
45143 DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
45144 DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
45145 DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks);
45148 drflac_uint32 countRemaining;
45149 const char* pRunningData;
45150 } drflac_vorbis_comment_iterator;
45151 DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments);
45152 DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut);
45155 drflac_uint32 countRemaining;
45156 const char* pRunningData;
45157 } drflac_cuesheet_track_iterator;
45161 drflac_uint64 offset;
45162 drflac_uint8 index;
45163 drflac_uint8 reserved[3];
45164 } drflac_cuesheet_track_index;
45168 drflac_uint64 offset;
45169 drflac_uint8 trackNumber;
45171 drflac_bool8 isAudio;
45172 drflac_bool8 preEmphasis;
45173 drflac_uint8 indexCount;
45174 const drflac_cuesheet_track_index* pIndexPoints;
45175 } drflac_cuesheet_track;
45176 DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData);
45177 DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack);
45182 /* dr_flac_h end */
45183 #endif /* MA_NO_FLAC */
45185 #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)
45186 /* dr_mp3_h begin */
45192 #define DRMP3_STRINGIFY(x) #x
45193 #define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x)
45194 #define DRMP3_VERSION_MAJOR 0
45195 #define DRMP3_VERSION_MINOR 6
45196 #define DRMP3_VERSION_REVISION 27
45197 #define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION)
45198 #include <stddef.h>
45199 typedef signed char drmp3_int8;
45200 typedef unsigned char drmp3_uint8;
45201 typedef signed short drmp3_int16;
45202 typedef unsigned short drmp3_uint16;
45203 typedef signed int drmp3_int32;
45204 typedef unsigned int drmp3_uint32;
45205 #if defined(_MSC_VER)
45206 typedef signed __int64 drmp3_int64;
45207 typedef unsigned __int64 drmp3_uint64;
45209 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
45210 #pragma GCC diagnostic push
45211 #pragma GCC diagnostic ignored "-Wlong-long"
45212 #if defined(__clang__)
45213 #pragma GCC diagnostic ignored "-Wc++11-long-long"
45216 typedef signed long long drmp3_int64;
45217 typedef unsigned long long drmp3_uint64;
45218 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
45219 #pragma GCC diagnostic pop
45222 #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
45223 typedef drmp3_uint64 drmp3_uintptr;
45225 typedef drmp3_uint32 drmp3_uintptr;
45227 typedef drmp3_uint8 drmp3_bool8;
45228 typedef drmp3_uint32 drmp3_bool32;
45229 #define DRMP3_TRUE 1
45230 #define DRMP3_FALSE 0
45231 #if !defined(DRMP3_API)
45232 #if defined(DRMP3_DLL)
45233 #if defined(_WIN32)
45234 #define DRMP3_DLL_IMPORT __declspec(dllimport)
45235 #define DRMP3_DLL_EXPORT __declspec(dllexport)
45236 #define DRMP3_DLL_PRIVATE static
45238 #if defined(__GNUC__) && __GNUC__ >= 4
45239 #define DRMP3_DLL_IMPORT __attribute__((visibility("default")))
45240 #define DRMP3_DLL_EXPORT __attribute__((visibility("default")))
45241 #define DRMP3_DLL_PRIVATE __attribute__((visibility("hidden")))
45243 #define DRMP3_DLL_IMPORT
45244 #define DRMP3_DLL_EXPORT
45245 #define DRMP3_DLL_PRIVATE static
45248 #if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION)
45249 #define DRMP3_API DRMP3_DLL_EXPORT
45251 #define DRMP3_API DRMP3_DLL_IMPORT
45253 #define DRMP3_PRIVATE DRMP3_DLL_PRIVATE
45255 #define DRMP3_API extern
45256 #define DRMP3_PRIVATE static
45259 typedef drmp3_int32 drmp3_result;
45260 #define DRMP3_SUCCESS 0
45261 #define DRMP3_ERROR -1
45262 #define DRMP3_INVALID_ARGS -2
45263 #define DRMP3_INVALID_OPERATION -3
45264 #define DRMP3_OUT_OF_MEMORY -4
45265 #define DRMP3_OUT_OF_RANGE -5
45266 #define DRMP3_ACCESS_DENIED -6
45267 #define DRMP3_DOES_NOT_EXIST -7
45268 #define DRMP3_ALREADY_EXISTS -8
45269 #define DRMP3_TOO_MANY_OPEN_FILES -9
45270 #define DRMP3_INVALID_FILE -10
45271 #define DRMP3_TOO_BIG -11
45272 #define DRMP3_PATH_TOO_LONG -12
45273 #define DRMP3_NAME_TOO_LONG -13
45274 #define DRMP3_NOT_DIRECTORY -14
45275 #define DRMP3_IS_DIRECTORY -15
45276 #define DRMP3_DIRECTORY_NOT_EMPTY -16
45277 #define DRMP3_END_OF_FILE -17
45278 #define DRMP3_NO_SPACE -18
45279 #define DRMP3_BUSY -19
45280 #define DRMP3_IO_ERROR -20
45281 #define DRMP3_INTERRUPT -21
45282 #define DRMP3_UNAVAILABLE -22
45283 #define DRMP3_ALREADY_IN_USE -23
45284 #define DRMP3_BAD_ADDRESS -24
45285 #define DRMP3_BAD_SEEK -25
45286 #define DRMP3_BAD_PIPE -26
45287 #define DRMP3_DEADLOCK -27
45288 #define DRMP3_TOO_MANY_LINKS -28
45289 #define DRMP3_NOT_IMPLEMENTED -29
45290 #define DRMP3_NO_MESSAGE -30
45291 #define DRMP3_BAD_MESSAGE -31
45292 #define DRMP3_NO_DATA_AVAILABLE -32
45293 #define DRMP3_INVALID_DATA -33
45294 #define DRMP3_TIMEOUT -34
45295 #define DRMP3_NO_NETWORK -35
45296 #define DRMP3_NOT_UNIQUE -36
45297 #define DRMP3_NOT_SOCKET -37
45298 #define DRMP3_NO_ADDRESS -38
45299 #define DRMP3_BAD_PROTOCOL -39
45300 #define DRMP3_PROTOCOL_UNAVAILABLE -40
45301 #define DRMP3_PROTOCOL_NOT_SUPPORTED -41
45302 #define DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED -42
45303 #define DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED -43
45304 #define DRMP3_SOCKET_NOT_SUPPORTED -44
45305 #define DRMP3_CONNECTION_RESET -45
45306 #define DRMP3_ALREADY_CONNECTED -46
45307 #define DRMP3_NOT_CONNECTED -47
45308 #define DRMP3_CONNECTION_REFUSED -48
45309 #define DRMP3_NO_HOST -49
45310 #define DRMP3_IN_PROGRESS -50
45311 #define DRMP3_CANCELLED -51
45312 #define DRMP3_MEMORY_ALREADY_MAPPED -52
45313 #define DRMP3_AT_END -53
45314 #define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152
45315 #define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2)
45317 #define DRMP3_INLINE __forceinline
45318 #elif defined(__GNUC__)
45319 #if defined(__STRICT_ANSI__)
45320 #define DRMP3_INLINE __inline__ __attribute__((always_inline))
45322 #define DRMP3_INLINE inline __attribute__((always_inline))
45324 #elif defined(__WATCOMC__)
45325 #define DRMP3_INLINE __inline
45327 #define DRMP3_INLINE
45329 DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision);
45330 DRMP3_API const char* drmp3_version_string(void);
45333 int frame_bytes, channels, hz, layer, bitrate_kbps;
45334 } drmp3dec_frame_info;
45337 float mdct_overlap[2][9*32], qmf_state[15*2*32];
45338 int reserv, free_format_bytes;
45339 drmp3_uint8 header[4], reserv_buf[511];
45341 DRMP3_API void drmp3dec_init(drmp3dec *dec);
45342 DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info);
45343 DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples);
45346 drmp3_seek_origin_start,
45347 drmp3_seek_origin_current
45348 } drmp3_seek_origin;
45351 drmp3_uint64 seekPosInBytes;
45352 drmp3_uint64 pcmFrameIndex;
45353 drmp3_uint16 mp3FramesToDiscard;
45354 drmp3_uint16 pcmFramesToDiscard;
45355 } drmp3_seek_point;
45356 typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
45357 typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin);
45361 void* (* onMalloc)(size_t sz, void* pUserData);
45362 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
45363 void (* onFree)(void* p, void* pUserData);
45364 } drmp3_allocation_callbacks;
45367 drmp3_uint32 channels;
45368 drmp3_uint32 sampleRate;
45373 drmp3dec_frame_info frameInfo;
45374 drmp3_uint32 channels;
45375 drmp3_uint32 sampleRate;
45376 drmp3_read_proc onRead;
45377 drmp3_seek_proc onSeek;
45379 drmp3_allocation_callbacks allocationCallbacks;
45380 drmp3_uint32 mp3FrameChannels;
45381 drmp3_uint32 mp3FrameSampleRate;
45382 drmp3_uint32 pcmFramesConsumedInMP3Frame;
45383 drmp3_uint32 pcmFramesRemainingInMP3Frame;
45384 drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME];
45385 drmp3_uint64 currentPCMFrame;
45386 drmp3_uint64 streamCursor;
45387 drmp3_seek_point* pSeekPoints;
45388 drmp3_uint32 seekPointCount;
45390 size_t dataCapacity;
45391 size_t dataConsumed;
45392 drmp3_uint8* pData;
45393 drmp3_bool32 atEnd : 1;
45396 const drmp3_uint8* pData;
45398 size_t currentReadPos;
45401 DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks);
45402 DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks);
45403 #ifndef DR_MP3_NO_STDIO
45404 DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks);
45405 DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks);
45407 DRMP3_API void drmp3_uninit(drmp3* pMP3);
45408 DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut);
45409 DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut);
45410 DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex);
45411 DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3);
45412 DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3);
45413 DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount);
45414 DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints);
45415 DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints);
45416 DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
45417 DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
45418 DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
45419 DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
45420 #ifndef DR_MP3_NO_STDIO
45421 DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
45422 DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
45424 DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks);
45425 DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks);
45431 #endif /* MA_NO_MP3 */
45434 /**************************************************************************************************************************************************************
45438 **************************************************************************************************************************************************************/
45439 #ifndef MA_NO_DECODING
45441 static size_t ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
45445 MA_ASSERT(pDecoder != NULL);
45446 MA_ASSERT(pBufferOut != NULL);
45448 bytesRead = pDecoder->onRead(pDecoder, pBufferOut, bytesToRead);
45449 pDecoder->readPointerInBytes += bytesRead;
45454 static ma_bool32 ma_decoder_seek_bytes(ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin)
45456 ma_bool32 wasSuccessful;
45458 MA_ASSERT(pDecoder != NULL);
45460 wasSuccessful = pDecoder->onSeek(pDecoder, byteOffset, origin);
45461 if (wasSuccessful) {
45462 if (origin == ma_seek_origin_start) {
45463 pDecoder->readPointerInBytes = (ma_uint64)byteOffset;
45465 pDecoder->readPointerInBytes += byteOffset;
45469 return wasSuccessful;
45473 MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)
45475 ma_decoder_config config;
45476 MA_ZERO_OBJECT(&config);
45477 config.format = outputFormat;
45478 config.channels = ma_min(outputChannels, ma_countof(config.channelMap));
45479 config.sampleRate = outputSampleRate;
45480 config.resampling.algorithm = ma_resample_algorithm_linear;
45481 config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
45482 config.resampling.speex.quality = 3;
45484 /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */
45489 MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig)
45491 ma_decoder_config config;
45492 if (pConfig != NULL) {
45495 MA_ZERO_OBJECT(&config);
45501 static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig)
45503 ma_data_converter_config converterConfig;
45505 MA_ASSERT(pDecoder != NULL);
45506 MA_ASSERT(pConfig != NULL);
45508 /* Make sure we're not asking for too many channels. */
45509 if (pConfig->channels > MA_MAX_CHANNELS) {
45510 return MA_INVALID_ARGS;
45513 /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */
45514 if (pDecoder->internalChannels > MA_MAX_CHANNELS) {
45515 return MA_INVALID_ARGS;
45519 /* Output format. */
45520 if (pConfig->format == ma_format_unknown) {
45521 pDecoder->outputFormat = pDecoder->internalFormat;
45523 pDecoder->outputFormat = pConfig->format;
45526 if (pConfig->channels == 0) {
45527 pDecoder->outputChannels = pDecoder->internalChannels;
45529 pDecoder->outputChannels = pConfig->channels;
45532 if (pConfig->sampleRate == 0) {
45533 pDecoder->outputSampleRate = pDecoder->internalSampleRate;
45535 pDecoder->outputSampleRate = pConfig->sampleRate;
45538 if (ma_channel_map_blank(pDecoder->outputChannels, pConfig->channelMap)) {
45539 ma_get_standard_channel_map(ma_standard_channel_map_default, pDecoder->outputChannels, pDecoder->outputChannelMap);
45541 MA_COPY_MEMORY(pDecoder->outputChannelMap, pConfig->channelMap, sizeof(pConfig->channelMap));
45545 converterConfig = ma_data_converter_config_init(
45546 pDecoder->internalFormat, pDecoder->outputFormat,
45547 pDecoder->internalChannels, pDecoder->outputChannels,
45548 pDecoder->internalSampleRate, pDecoder->outputSampleRate
45550 ma_channel_map_copy(converterConfig.channelMapIn, pDecoder->internalChannelMap, pDecoder->internalChannels);
45551 ma_channel_map_copy(converterConfig.channelMapOut, pDecoder->outputChannelMap, pDecoder->outputChannels);
45552 converterConfig.channelMixMode = pConfig->channelMixMode;
45553 converterConfig.ditherMode = pConfig->ditherMode;
45554 converterConfig.resampling.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */
45555 converterConfig.resampling.algorithm = pConfig->resampling.algorithm;
45556 converterConfig.resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder;
45557 converterConfig.resampling.speex.quality = pConfig->resampling.speex.quality;
45559 return ma_data_converter_init(&converterConfig, &pDecoder->converter);
45566 static size_t ma_decoder_internal_on_read__wav(void* pUserData, void* pBufferOut, size_t bytesToRead)
45568 ma_decoder* pDecoder = (ma_decoder*)pUserData;
45569 MA_ASSERT(pDecoder != NULL);
45571 return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead);
45574 static drwav_bool32 ma_decoder_internal_on_seek__wav(void* pUserData, int offset, drwav_seek_origin origin)
45576 ma_decoder* pDecoder = (ma_decoder*)pUserData;
45577 MA_ASSERT(pDecoder != NULL);
45579 return ma_decoder_seek_bytes(pDecoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
45582 static ma_uint64 ma_decoder_internal_on_read_pcm_frames__wav(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
45586 MA_ASSERT(pDecoder != NULL);
45587 MA_ASSERT(pFramesOut != NULL);
45589 pWav = (drwav*)pDecoder->pInternalDecoder;
45590 MA_ASSERT(pWav != NULL);
45592 switch (pDecoder->internalFormat) {
45593 case ma_format_s16: return drwav_read_pcm_frames_s16(pWav, frameCount, (drwav_int16*)pFramesOut);
45594 case ma_format_s32: return drwav_read_pcm_frames_s32(pWav, frameCount, (drwav_int32*)pFramesOut);
45595 case ma_format_f32: return drwav_read_pcm_frames_f32(pWav, frameCount, (float*)pFramesOut);
45599 /* Should never get here. If we do, it means the internal format was not set correctly at initialization time. */
45600 MA_ASSERT(MA_FALSE);
45604 static ma_result ma_decoder_internal_on_seek_to_pcm_frame__wav(ma_decoder* pDecoder, ma_uint64 frameIndex)
45607 drwav_bool32 result;
45609 pWav = (drwav*)pDecoder->pInternalDecoder;
45610 MA_ASSERT(pWav != NULL);
45612 result = drwav_seek_to_pcm_frame(pWav, frameIndex);
45620 static ma_result ma_decoder_internal_on_uninit__wav(ma_decoder* pDecoder)
45622 drwav_uninit((drwav*)pDecoder->pInternalDecoder);
45623 ma__free_from_callbacks(pDecoder->pInternalDecoder, &pDecoder->allocationCallbacks);
45627 static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__wav(ma_decoder* pDecoder)
45629 return ((drwav*)pDecoder->pInternalDecoder)->totalPCMFrameCount;
45632 static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
45635 drwav_allocation_callbacks allocationCallbacks;
45637 MA_ASSERT(pConfig != NULL);
45638 MA_ASSERT(pDecoder != NULL);
45642 pWav = (drwav*)ma__malloc_from_callbacks(sizeof(*pWav), &pDecoder->allocationCallbacks);
45643 if (pWav == NULL) {
45644 return MA_OUT_OF_MEMORY;
45647 allocationCallbacks.pUserData = pDecoder->allocationCallbacks.pUserData;
45648 allocationCallbacks.onMalloc = pDecoder->allocationCallbacks.onMalloc;
45649 allocationCallbacks.onRealloc = pDecoder->allocationCallbacks.onRealloc;
45650 allocationCallbacks.onFree = pDecoder->allocationCallbacks.onFree;
45652 /* Try opening the decoder first. */
45653 if (!drwav_init(pWav, ma_decoder_internal_on_read__wav, ma_decoder_internal_on_seek__wav, pDecoder, &allocationCallbacks)) {
45654 ma__free_from_callbacks(pWav, &pDecoder->allocationCallbacks);
45658 /* If we get here it means we successfully initialized the WAV decoder. We can now initialize the rest of the ma_decoder. */
45659 pDecoder->onReadPCMFrames = ma_decoder_internal_on_read_pcm_frames__wav;
45660 pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__wav;
45661 pDecoder->onUninit = ma_decoder_internal_on_uninit__wav;
45662 pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__wav;
45663 pDecoder->pInternalDecoder = pWav;
45665 /* Try to be as optimal as possible for the internal format. If miniaudio does not support a format we will fall back to f32. */
45666 pDecoder->internalFormat = ma_format_unknown;
45667 switch (pWav->translatedFormatTag) {
45668 case DR_WAVE_FORMAT_PCM:
45670 if (pWav->bitsPerSample == 8) {
45671 pDecoder->internalFormat = ma_format_s16;
45672 } else if (pWav->bitsPerSample == 16) {
45673 pDecoder->internalFormat = ma_format_s16;
45674 } else if (pWav->bitsPerSample == 32) {
45675 pDecoder->internalFormat = ma_format_s32;
45679 case DR_WAVE_FORMAT_IEEE_FLOAT:
45681 if (pWav->bitsPerSample == 32) {
45682 pDecoder->internalFormat = ma_format_f32;
45686 case DR_WAVE_FORMAT_ALAW:
45687 case DR_WAVE_FORMAT_MULAW:
45688 case DR_WAVE_FORMAT_ADPCM:
45689 case DR_WAVE_FORMAT_DVI_ADPCM:
45691 pDecoder->internalFormat = ma_format_s16;
45695 if (pDecoder->internalFormat == ma_format_unknown) {
45696 pDecoder->internalFormat = ma_format_f32;
45699 pDecoder->internalChannels = pWav->channels;
45700 pDecoder->internalSampleRate = pWav->sampleRate;
45701 ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDecoder->internalChannels, pDecoder->internalChannelMap);
45705 #endif /* dr_wav_h */
45709 #define MA_HAS_FLAC
45711 static size_t ma_decoder_internal_on_read__flac(void* pUserData, void* pBufferOut, size_t bytesToRead)
45713 ma_decoder* pDecoder = (ma_decoder*)pUserData;
45714 MA_ASSERT(pDecoder != NULL);
45716 return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead);
45719 static drflac_bool32 ma_decoder_internal_on_seek__flac(void* pUserData, int offset, drflac_seek_origin origin)
45721 ma_decoder* pDecoder = (ma_decoder*)pUserData;
45722 MA_ASSERT(pDecoder != NULL);
45724 return ma_decoder_seek_bytes(pDecoder, offset, (origin == drflac_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
45727 static ma_uint64 ma_decoder_internal_on_read_pcm_frames__flac(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
45731 MA_ASSERT(pDecoder != NULL);
45732 MA_ASSERT(pFramesOut != NULL);
45734 pFlac = (drflac*)pDecoder->pInternalDecoder;
45735 MA_ASSERT(pFlac != NULL);
45737 switch (pDecoder->internalFormat) {
45738 case ma_format_s16: return drflac_read_pcm_frames_s16(pFlac, frameCount, (drflac_int16*)pFramesOut);
45739 case ma_format_s32: return drflac_read_pcm_frames_s32(pFlac, frameCount, (drflac_int32*)pFramesOut);
45740 case ma_format_f32: return drflac_read_pcm_frames_f32(pFlac, frameCount, (float*)pFramesOut);
45744 /* Should never get here. If we do, it means the internal format was not set correctly at initialization time. */
45745 MA_ASSERT(MA_FALSE);
45749 static ma_result ma_decoder_internal_on_seek_to_pcm_frame__flac(ma_decoder* pDecoder, ma_uint64 frameIndex)
45752 drflac_bool32 result;
45754 pFlac = (drflac*)pDecoder->pInternalDecoder;
45755 MA_ASSERT(pFlac != NULL);
45757 result = drflac_seek_to_pcm_frame(pFlac, frameIndex);
45765 static ma_result ma_decoder_internal_on_uninit__flac(ma_decoder* pDecoder)
45767 drflac_close((drflac*)pDecoder->pInternalDecoder);
45771 static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__flac(ma_decoder* pDecoder)
45773 return ((drflac*)pDecoder->pInternalDecoder)->totalPCMFrameCount;
45776 static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
45779 drflac_allocation_callbacks allocationCallbacks;
45781 MA_ASSERT(pConfig != NULL);
45782 MA_ASSERT(pDecoder != NULL);
45784 allocationCallbacks.pUserData = pDecoder->allocationCallbacks.pUserData;
45785 allocationCallbacks.onMalloc = pDecoder->allocationCallbacks.onMalloc;
45786 allocationCallbacks.onRealloc = pDecoder->allocationCallbacks.onRealloc;
45787 allocationCallbacks.onFree = pDecoder->allocationCallbacks.onFree;
45789 /* Try opening the decoder first. */
45790 pFlac = drflac_open(ma_decoder_internal_on_read__flac, ma_decoder_internal_on_seek__flac, pDecoder, &allocationCallbacks);
45791 if (pFlac == NULL) {
45795 /* If we get here it means we successfully initialized the FLAC decoder. We can now initialize the rest of the ma_decoder. */
45796 pDecoder->onReadPCMFrames = ma_decoder_internal_on_read_pcm_frames__flac;
45797 pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__flac;
45798 pDecoder->onUninit = ma_decoder_internal_on_uninit__flac;
45799 pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__flac;
45800 pDecoder->pInternalDecoder = pFlac;
45803 dr_flac supports reading as s32, s16 and f32. Try to do a one-to-one mapping if possible, but fall back to s32 if not. s32 is the "native" FLAC format
45804 since it's the only one that's truly lossless. If the internal bits per sample is <= 16 we will decode to ma_format_s16 to keep it more efficient.
45806 if (pConfig->format == ma_format_unknown) {
45807 if (pFlac->bitsPerSample <= 16) {
45808 pDecoder->internalFormat = ma_format_s16;
45810 pDecoder->internalFormat = ma_format_s32;
45813 if (pConfig->format == ma_format_s16 || pConfig->format == ma_format_f32) {
45814 pDecoder->internalFormat = pConfig->format;
45816 pDecoder->internalFormat = ma_format_s32; /* s32 as the baseline to ensure no loss of precision for 24-bit encoded files. */
45820 pDecoder->internalChannels = pFlac->channels;
45821 pDecoder->internalSampleRate = pFlac->sampleRate;
45822 ma_get_standard_channel_map(ma_standard_channel_map_flac, pDecoder->internalChannels, pDecoder->internalChannelMap);
45826 #endif /* dr_flac_h */
45832 static size_t ma_decoder_internal_on_read__mp3(void* pUserData, void* pBufferOut, size_t bytesToRead)
45834 ma_decoder* pDecoder = (ma_decoder*)pUserData;
45835 MA_ASSERT(pDecoder != NULL);
45837 return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead);
45840 static drmp3_bool32 ma_decoder_internal_on_seek__mp3(void* pUserData, int offset, drmp3_seek_origin origin)
45842 ma_decoder* pDecoder = (ma_decoder*)pUserData;
45843 MA_ASSERT(pDecoder != NULL);
45845 return ma_decoder_seek_bytes(pDecoder, offset, (origin == drmp3_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
45848 static ma_uint64 ma_decoder_internal_on_read_pcm_frames__mp3(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
45852 MA_ASSERT(pDecoder != NULL);
45853 MA_ASSERT(pFramesOut != NULL);
45855 pMP3 = (drmp3*)pDecoder->pInternalDecoder;
45856 MA_ASSERT(pMP3 != NULL);
45858 #if defined(DR_MP3_FLOAT_OUTPUT)
45859 MA_ASSERT(pDecoder->internalFormat == ma_format_f32);
45860 return drmp3_read_pcm_frames_f32(pMP3, frameCount, (float*)pFramesOut);
45862 MA_ASSERT(pDecoder->internalFormat == ma_format_s16);
45863 return drmp3_read_pcm_frames_s16(pMP3, frameCount, (drmp3_int16*)pFramesOut);
45867 static ma_result ma_decoder_internal_on_seek_to_pcm_frame__mp3(ma_decoder* pDecoder, ma_uint64 frameIndex)
45870 drmp3_bool32 result;
45872 pMP3 = (drmp3*)pDecoder->pInternalDecoder;
45873 MA_ASSERT(pMP3 != NULL);
45875 result = drmp3_seek_to_pcm_frame(pMP3, frameIndex);
45883 static ma_result ma_decoder_internal_on_uninit__mp3(ma_decoder* pDecoder)
45885 drmp3_uninit((drmp3*)pDecoder->pInternalDecoder);
45886 ma__free_from_callbacks(pDecoder->pInternalDecoder, &pDecoder->allocationCallbacks);
45890 static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__mp3(ma_decoder* pDecoder)
45892 return drmp3_get_pcm_frame_count((drmp3*)pDecoder->pInternalDecoder);
45895 static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
45898 drmp3_allocation_callbacks allocationCallbacks;
45900 MA_ASSERT(pConfig != NULL);
45901 MA_ASSERT(pDecoder != NULL);
45905 pMP3 = (drmp3*)ma__malloc_from_callbacks(sizeof(*pMP3), &pDecoder->allocationCallbacks);
45906 if (pMP3 == NULL) {
45907 return MA_OUT_OF_MEMORY;
45910 allocationCallbacks.pUserData = pDecoder->allocationCallbacks.pUserData;
45911 allocationCallbacks.onMalloc = pDecoder->allocationCallbacks.onMalloc;
45912 allocationCallbacks.onRealloc = pDecoder->allocationCallbacks.onRealloc;
45913 allocationCallbacks.onFree = pDecoder->allocationCallbacks.onFree;
45916 Try opening the decoder first. We always use whatever dr_mp3 reports for channel count and sample rate. The format is determined by
45917 the presence of DR_MP3_FLOAT_OUTPUT.
45919 if (!drmp3_init(pMP3, ma_decoder_internal_on_read__mp3, ma_decoder_internal_on_seek__mp3, pDecoder, &allocationCallbacks)) {
45920 ma__free_from_callbacks(pMP3, &pDecoder->allocationCallbacks);
45924 /* If we get here it means we successfully initialized the MP3 decoder. We can now initialize the rest of the ma_decoder. */
45925 pDecoder->onReadPCMFrames = ma_decoder_internal_on_read_pcm_frames__mp3;
45926 pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__mp3;
45927 pDecoder->onUninit = ma_decoder_internal_on_uninit__mp3;
45928 pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__mp3;
45929 pDecoder->pInternalDecoder = pMP3;
45931 /* Internal format. */
45932 #if defined(DR_MP3_FLOAT_OUTPUT)
45933 pDecoder->internalFormat = ma_format_f32;
45935 pDecoder->internalFormat = ma_format_s16;
45937 pDecoder->internalChannels = pMP3->channels;
45938 pDecoder->internalSampleRate = pMP3->sampleRate;
45939 ma_get_standard_channel_map(ma_standard_channel_map_default, pDecoder->internalChannels, pDecoder->internalChannelMap);
45943 #endif /* dr_mp3_h */
45946 #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
45947 #define MA_HAS_VORBIS
45949 /* The size in bytes of each chunk of data to read from the Vorbis stream. */
45950 #define MA_VORBIS_DATA_CHUNK_SIZE 4096
45954 stb_vorbis* pInternalVorbis;
45957 size_t dataCapacity;
45958 ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */
45959 ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */
45960 float** ppPacketData;
45961 } ma_vorbis_decoder;
45963 static ma_uint64 ma_vorbis_decoder_read_pcm_frames(ma_vorbis_decoder* pVorbis, ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
45965 float* pFramesOutF;
45966 ma_uint64 totalFramesRead;
45968 MA_ASSERT(pVorbis != NULL);
45969 MA_ASSERT(pDecoder != NULL);
45971 pFramesOutF = (float*)pFramesOut;
45973 totalFramesRead = 0;
45974 while (frameCount > 0) {
45975 /* Read from the in-memory buffer first. */
45976 ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->framesRemaining, frameCount); /* Safe cast because pVorbis->framesRemaining is 32-bit. */
45978 if (pFramesOut != NULL) {
45980 for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) {
45981 ma_uint32 iChannel;
45982 for (iChannel = 0; iChannel < pDecoder->internalChannels; ++iChannel) {
45983 pFramesOutF[iChannel] = pVorbis->ppPacketData[iChannel][pVorbis->framesConsumed+iFrame];
45985 pFramesOutF += pDecoder->internalChannels;
45989 pVorbis->framesConsumed += framesToReadFromCache;
45990 pVorbis->framesRemaining -= framesToReadFromCache;
45991 frameCount -= framesToReadFromCache;
45992 totalFramesRead += framesToReadFromCache;
45994 if (frameCount == 0) {
45998 MA_ASSERT(pVorbis->framesRemaining == 0);
46000 /* We've run out of cached frames, so decode the next packet and continue iteration. */
46004 int consumedDataSize;
46006 if (pVorbis->dataSize > INT_MAX) {
46007 break; /* Too big. */
46011 consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->pInternalVorbis, pVorbis->pData, (int)pVorbis->dataSize, NULL, (float***)&pVorbis->ppPacketData, &samplesRead);
46012 if (consumedDataSize != 0) {
46013 size_t leftoverDataSize = (pVorbis->dataSize - (size_t)consumedDataSize);
46015 for (i = 0; i < leftoverDataSize; ++i) {
46016 pVorbis->pData[i] = pVorbis->pData[i + consumedDataSize];
46019 pVorbis->dataSize = leftoverDataSize;
46020 pVorbis->framesConsumed = 0;
46021 pVorbis->framesRemaining = samplesRead;
46024 /* Need more data. If there's any room in the existing buffer allocation fill that first. Otherwise expand. */
46026 if (pVorbis->dataCapacity == pVorbis->dataSize) {
46027 /* No room. Expand. */
46028 size_t oldCap = pVorbis->dataCapacity;
46029 size_t newCap = pVorbis->dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE;
46030 ma_uint8* pNewData;
46032 pNewData = (ma_uint8*)ma__realloc_from_callbacks(pVorbis->pData, newCap, oldCap, &pDecoder->allocationCallbacks);
46033 if (pNewData == NULL) {
46034 return totalFramesRead; /* Out of memory. */
46037 pVorbis->pData = pNewData;
46038 pVorbis->dataCapacity = newCap;
46041 /* Fill in a chunk. */
46042 bytesRead = ma_decoder_read_bytes(pDecoder, pVorbis->pData + pVorbis->dataSize, (pVorbis->dataCapacity - pVorbis->dataSize));
46043 if (bytesRead == 0) {
46044 return totalFramesRead; /* Error reading more data. */
46047 pVorbis->dataSize += bytesRead;
46052 return totalFramesRead;
46055 static ma_result ma_vorbis_decoder_seek_to_pcm_frame(ma_vorbis_decoder* pVorbis, ma_decoder* pDecoder, ma_uint64 frameIndex)
46057 float buffer[4096];
46059 MA_ASSERT(pVorbis != NULL);
46060 MA_ASSERT(pDecoder != NULL);
46063 This is terribly inefficient because stb_vorbis does not have a good seeking solution with it's push API. Currently this just performs
46064 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
46065 find the one containing the sample we need. Then we know exactly where to seek for stb_vorbis.
46067 TODO: Use seeking logic documented for stb_vorbis_flush_pushdata().
46069 if (!ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start)) {
46073 stb_vorbis_flush_pushdata(pVorbis->pInternalVorbis);
46074 pVorbis->framesConsumed = 0;
46075 pVorbis->framesRemaining = 0;
46076 pVorbis->dataSize = 0;
46078 while (frameIndex > 0) {
46079 ma_uint32 framesRead;
46080 ma_uint32 framesToRead = ma_countof(buffer)/pDecoder->internalChannels;
46081 if (framesToRead > frameIndex) {
46082 framesToRead = (ma_uint32)frameIndex;
46085 framesRead = (ma_uint32)ma_vorbis_decoder_read_pcm_frames(pVorbis, pDecoder, buffer, framesToRead);
46086 if (framesRead == 0) {
46090 frameIndex -= framesRead;
46097 static ma_result ma_decoder_internal_on_seek_to_pcm_frame__vorbis(ma_decoder* pDecoder, ma_uint64 frameIndex)
46099 ma_vorbis_decoder* pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
46100 MA_ASSERT(pVorbis != NULL);
46102 return ma_vorbis_decoder_seek_to_pcm_frame(pVorbis, pDecoder, frameIndex);
46105 static ma_result ma_decoder_internal_on_uninit__vorbis(ma_decoder* pDecoder)
46107 ma_vorbis_decoder* pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
46108 MA_ASSERT(pVorbis != NULL);
46110 stb_vorbis_close(pVorbis->pInternalVorbis);
46111 ma__free_from_callbacks(pVorbis->pData, &pDecoder->allocationCallbacks);
46112 ma__free_from_callbacks(pVorbis, &pDecoder->allocationCallbacks);
46117 static ma_uint64 ma_decoder_internal_on_read_pcm_frames__vorbis(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
46119 ma_vorbis_decoder* pVorbis;
46121 MA_ASSERT(pDecoder != NULL);
46122 MA_ASSERT(pFramesOut != NULL);
46123 MA_ASSERT(pDecoder->internalFormat == ma_format_f32);
46125 pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
46126 MA_ASSERT(pVorbis != NULL);
46128 return ma_vorbis_decoder_read_pcm_frames(pVorbis, pDecoder, pFramesOut, frameCount);
46131 static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__vorbis(ma_decoder* pDecoder)
46133 /* No good way to do this with Vorbis. */
46138 static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
46140 stb_vorbis* pInternalVorbis = NULL;
46141 size_t dataSize = 0;
46142 size_t dataCapacity = 0;
46143 ma_uint8* pData = NULL;
46144 stb_vorbis_info vorbisInfo;
46145 size_t vorbisDataSize;
46146 ma_vorbis_decoder* pVorbis;
46148 MA_ASSERT(pConfig != NULL);
46149 MA_ASSERT(pDecoder != NULL);
46151 /* We grow the buffer in chunks. */
46154 /* Allocate memory for a new chunk. */
46155 ma_uint8* pNewData;
46157 int vorbisError = 0;
46158 int consumedDataSize = 0;
46159 size_t oldCapacity = dataCapacity;
46161 dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE;
46162 pNewData = (ma_uint8*)ma__realloc_from_callbacks(pData, dataCapacity, oldCapacity, &pDecoder->allocationCallbacks);
46163 if (pNewData == NULL) {
46164 ma__free_from_callbacks(pData, &pDecoder->allocationCallbacks);
46165 return MA_OUT_OF_MEMORY;
46170 /* Fill in a chunk. */
46171 bytesRead = ma_decoder_read_bytes(pDecoder, pData + dataSize, (dataCapacity - dataSize));
46172 if (bytesRead == 0) {
46176 dataSize += bytesRead;
46177 if (dataSize > INT_MAX) {
46178 return MA_ERROR; /* Too big. */
46181 pInternalVorbis = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL);
46182 if (pInternalVorbis != NULL) {
46184 If we get here it means we were able to open the stb_vorbis decoder. There may be some leftover bytes in our buffer, so
46185 we need to move those bytes down to the front of the buffer since they'll be needed for future decoding.
46187 size_t leftoverDataSize = (dataSize - (size_t)consumedDataSize);
46189 for (i = 0; i < leftoverDataSize; ++i) {
46190 pData[i] = pData[i + consumedDataSize];
46193 dataSize = leftoverDataSize;
46194 break; /* Success. */
46196 if (vorbisError == VORBIS_need_more_data) {
46199 return MA_ERROR; /* Failed to open the stb_vorbis decoder. */
46205 /* If we get here it means we successfully opened the Vorbis decoder. */
46206 vorbisInfo = stb_vorbis_get_info(pInternalVorbis);
46208 /* Don't allow more than MA_MAX_CHANNELS channels. */
46209 if (vorbisInfo.channels > MA_MAX_CHANNELS) {
46210 stb_vorbis_close(pInternalVorbis);
46211 ma__free_from_callbacks(pData, &pDecoder->allocationCallbacks);
46212 return MA_ERROR; /* Too many channels. */
46215 vorbisDataSize = sizeof(ma_vorbis_decoder) + sizeof(float)*vorbisInfo.max_frame_size;
46216 pVorbis = (ma_vorbis_decoder*)ma__malloc_from_callbacks(vorbisDataSize, &pDecoder->allocationCallbacks);
46217 if (pVorbis == NULL) {
46218 stb_vorbis_close(pInternalVorbis);
46219 ma__free_from_callbacks(pData, &pDecoder->allocationCallbacks);
46220 return MA_OUT_OF_MEMORY;
46223 MA_ZERO_MEMORY(pVorbis, vorbisDataSize);
46224 pVorbis->pInternalVorbis = pInternalVorbis;
46225 pVorbis->pData = pData;
46226 pVorbis->dataSize = dataSize;
46227 pVorbis->dataCapacity = dataCapacity;
46229 pDecoder->onReadPCMFrames = ma_decoder_internal_on_read_pcm_frames__vorbis;
46230 pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__vorbis;
46231 pDecoder->onUninit = ma_decoder_internal_on_uninit__vorbis;
46232 pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__vorbis;
46233 pDecoder->pInternalDecoder = pVorbis;
46235 /* The internal format is always f32. */
46236 pDecoder->internalFormat = ma_format_f32;
46237 pDecoder->internalChannels = vorbisInfo.channels;
46238 pDecoder->internalSampleRate = vorbisInfo.sample_rate;
46239 ma_get_standard_channel_map(ma_standard_channel_map_vorbis, pDecoder->internalChannels, pDecoder->internalChannelMap);
46243 #endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */
46246 static ma_uint64 ma_decoder_internal_on_read_pcm_frames__raw(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
46249 ma_uint64 totalFramesRead;
46250 void* pRunningFramesOut;
46252 MA_ASSERT(pDecoder != NULL);
46254 /* For raw decoding we just read directly from the decoder's callbacks. */
46255 bpf = ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
46257 totalFramesRead = 0;
46258 pRunningFramesOut = pFramesOut;
46260 while (totalFramesRead < frameCount) {
46261 ma_uint64 framesReadThisIteration;
46262 ma_uint64 framesToReadThisIteration = (frameCount - totalFramesRead);
46263 if (framesToReadThisIteration > 0x7FFFFFFF/bpf) {
46264 framesToReadThisIteration = 0x7FFFFFFF/bpf;
46267 if (pFramesOut != NULL) {
46268 framesReadThisIteration = ma_decoder_read_bytes(pDecoder, pRunningFramesOut, (size_t)framesToReadThisIteration * bpf) / bpf; /* Safe cast to size_t. */
46269 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIteration * bpf);
46271 /* We'll first try seeking. If this fails it means the end was reached and we'll to do a read-and-discard slow path to get the exact amount. */
46272 if (ma_decoder_seek_bytes(pDecoder, (int)framesToReadThisIteration, ma_seek_origin_current)) {
46273 framesReadThisIteration = framesToReadThisIteration;
46275 /* Slow path. Need to fall back to a read-and-discard. This is required so we can get the exact number of remaining. */
46276 ma_uint8 buffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
46277 ma_uint32 bufferCap = sizeof(buffer) / bpf;
46279 framesReadThisIteration = 0;
46280 while (framesReadThisIteration < framesToReadThisIteration) {
46281 ma_uint64 framesReadNow;
46282 ma_uint64 framesToReadNow = framesToReadThisIteration - framesReadThisIteration;
46283 if (framesToReadNow > bufferCap) {
46284 framesToReadNow = bufferCap;
46287 framesReadNow = ma_decoder_read_bytes(pDecoder, buffer, (size_t)(framesToReadNow * bpf)) / bpf; /* Safe cast. */
46288 framesReadThisIteration += framesReadNow;
46290 if (framesReadNow < framesToReadNow) {
46291 break; /* The end has been reached. */
46297 totalFramesRead += framesReadThisIteration;
46299 if (framesReadThisIteration < framesToReadThisIteration) {
46304 return totalFramesRead;
46307 static ma_result ma_decoder_internal_on_seek_to_pcm_frame__raw(ma_decoder* pDecoder, ma_uint64 frameIndex)
46309 ma_bool32 result = MA_FALSE;
46310 ma_uint64 totalBytesToSeek;
46312 MA_ASSERT(pDecoder != NULL);
46314 if (pDecoder->onSeek == NULL) {
46318 /* The callback uses a 32 bit integer whereas we use a 64 bit unsigned integer. We just need to continuously seek until we're at the correct position. */
46319 totalBytesToSeek = frameIndex * ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
46320 if (totalBytesToSeek < 0x7FFFFFFF) {
46322 result = ma_decoder_seek_bytes(pDecoder, (int)(frameIndex * ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels)), ma_seek_origin_start);
46324 /* Complex case. Start by doing a seek relative to the start. Then keep looping using offset seeking. */
46325 result = ma_decoder_seek_bytes(pDecoder, 0x7FFFFFFF, ma_seek_origin_start);
46326 if (result == MA_TRUE) {
46327 totalBytesToSeek -= 0x7FFFFFFF;
46329 while (totalBytesToSeek > 0) {
46330 ma_uint64 bytesToSeekThisIteration = totalBytesToSeek;
46331 if (bytesToSeekThisIteration > 0x7FFFFFFF) {
46332 bytesToSeekThisIteration = 0x7FFFFFFF;
46335 result = ma_decoder_seek_bytes(pDecoder, (int)bytesToSeekThisIteration, ma_seek_origin_current);
46336 if (result != MA_TRUE) {
46340 totalBytesToSeek -= bytesToSeekThisIteration;
46352 static ma_result ma_decoder_internal_on_uninit__raw(ma_decoder* pDecoder)
46358 static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__raw(ma_decoder* pDecoder)
46364 static ma_result ma_decoder_init_raw__internal(const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder)
46366 MA_ASSERT(pConfigIn != NULL);
46367 MA_ASSERT(pConfigOut != NULL);
46368 MA_ASSERT(pDecoder != NULL);
46372 pDecoder->onReadPCMFrames = ma_decoder_internal_on_read_pcm_frames__raw;
46373 pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__raw;
46374 pDecoder->onUninit = ma_decoder_internal_on_uninit__raw;
46375 pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__raw;
46377 /* Internal format. */
46378 pDecoder->internalFormat = pConfigIn->format;
46379 pDecoder->internalChannels = pConfigIn->channels;
46380 pDecoder->internalSampleRate = pConfigIn->sampleRate;
46381 ma_channel_map_copy(pDecoder->internalChannelMap, pConfigIn->channelMap, pConfigIn->channels);
46386 static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
46388 MA_ASSERT(pDecoder != NULL);
46390 if (pConfig != NULL) {
46391 return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks);
46393 pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default();
46398 static ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
46400 ma_uint64 framesRead = ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount);
46402 if (pFramesRead != NULL) {
46403 *pFramesRead = framesRead;
46406 if (framesRead < frameCount) {
46413 static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
46415 return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex);
46418 static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
46420 ma_decoder* pDecoder = (ma_decoder*)pDataSource;
46422 *pFormat = pDecoder->outputFormat;
46423 *pChannels = pDecoder->outputChannels;
46424 *pSampleRate = pDecoder->outputSampleRate;
46429 static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pLength)
46431 ma_decoder* pDecoder = (ma_decoder*)pDataSource;
46433 return ma_decoder_get_cursor_in_pcm_frames(pDecoder, pLength);
46436 static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
46438 ma_decoder* pDecoder = (ma_decoder*)pDataSource;
46440 *pLength = ma_decoder_get_length_in_pcm_frames(pDecoder);
46441 if (*pLength == 0) {
46442 return MA_NOT_IMPLEMENTED;
46448 static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
46452 MA_ASSERT(pConfig != NULL);
46454 if (pDecoder == NULL) {
46455 return MA_INVALID_ARGS;
46458 MA_ZERO_OBJECT(pDecoder);
46460 if (onRead == NULL || onSeek == NULL) {
46461 return MA_INVALID_ARGS;
46464 pDecoder->ds.onRead = ma_decoder__data_source_on_read;
46465 pDecoder->ds.onSeek = ma_decoder__data_source_on_seek;
46466 pDecoder->ds.onGetDataFormat = ma_decoder__data_source_on_get_data_format;
46467 pDecoder->ds.onGetCursor = ma_decoder__data_source_on_get_cursor;
46468 pDecoder->ds.onGetLength = ma_decoder__data_source_on_get_length;
46470 pDecoder->onRead = onRead;
46471 pDecoder->onSeek = onSeek;
46472 pDecoder->pUserData = pUserData;
46474 result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder);
46475 if (result != MA_SUCCESS) {
46482 static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
46484 ma_result result = MA_SUCCESS;
46486 /* Basic validation in case the internal decoder supports different limits to miniaudio. */
46487 if (pDecoder->internalChannels < MA_MIN_CHANNELS || pDecoder->internalChannels > MA_MAX_CHANNELS) {
46488 result = MA_INVALID_DATA;
46491 if (result == MA_SUCCESS) {
46492 result = ma_decoder__init_data_converter(pDecoder, pConfig);
46495 /* If we failed post initialization we need to uninitialize the decoder before returning to prevent a memory leak. */
46496 if (result != MA_SUCCESS) {
46497 ma_decoder_uninit(pDecoder);
46504 MA_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)
46507 ma_decoder_config config;
46510 config = ma_decoder_config_init_copy(pConfig);
46512 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
46513 if (result != MA_SUCCESS) {
46517 result = ma_decoder_init_wav__internal(&config, pDecoder);
46518 if (result != MA_SUCCESS) {
46522 return ma_decoder__postinit(&config, pDecoder);
46529 return MA_NO_BACKEND;
46533 MA_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)
46536 ma_decoder_config config;
46539 config = ma_decoder_config_init_copy(pConfig);
46541 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
46542 if (result != MA_SUCCESS) {
46546 result = ma_decoder_init_flac__internal(&config, pDecoder);
46547 if (result != MA_SUCCESS) {
46551 return ma_decoder__postinit(&config, pDecoder);
46558 return MA_NO_BACKEND;
46562 MA_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)
46565 ma_decoder_config config;
46568 config = ma_decoder_config_init_copy(pConfig);
46570 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
46571 if (result != MA_SUCCESS) {
46575 result = ma_decoder_init_mp3__internal(&config, pDecoder);
46576 if (result != MA_SUCCESS) {
46580 return ma_decoder__postinit(&config, pDecoder);
46587 return MA_NO_BACKEND;
46591 MA_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)
46593 #ifdef MA_HAS_VORBIS
46594 ma_decoder_config config;
46597 config = ma_decoder_config_init_copy(pConfig);
46599 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
46600 if (result != MA_SUCCESS) {
46604 result = ma_decoder_init_vorbis__internal(&config, pDecoder);
46605 if (result != MA_SUCCESS) {
46609 return ma_decoder__postinit(&config, pDecoder);
46616 return MA_NO_BACKEND;
46620 MA_API ma_result ma_decoder_init_raw(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder)
46622 ma_decoder_config config;
46625 config = ma_decoder_config_init_copy(pConfigOut);
46627 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
46628 if (result != MA_SUCCESS) {
46632 result = ma_decoder_init_raw__internal(pConfigIn, &config, pDecoder);
46633 if (result != MA_SUCCESS) {
46637 return ma_decoder__postinit(&config, pDecoder);
46640 static ma_result ma_decoder_init__internal(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
46642 ma_result result = MA_NO_BACKEND;
46644 MA_ASSERT(pConfig != NULL);
46645 MA_ASSERT(pDecoder != NULL);
46647 /* Silence some warnings in the case that we don't have any decoder backends enabled. */
46654 /* We use trial and error to open a decoder. */
46657 if (result != MA_SUCCESS) {
46658 result = ma_decoder_init_wav__internal(pConfig, pDecoder);
46659 if (result != MA_SUCCESS) {
46660 onSeek(pDecoder, 0, ma_seek_origin_start);
46665 if (result != MA_SUCCESS) {
46666 result = ma_decoder_init_flac__internal(pConfig, pDecoder);
46667 if (result != MA_SUCCESS) {
46668 onSeek(pDecoder, 0, ma_seek_origin_start);
46673 if (result != MA_SUCCESS) {
46674 result = ma_decoder_init_mp3__internal(pConfig, pDecoder);
46675 if (result != MA_SUCCESS) {
46676 onSeek(pDecoder, 0, ma_seek_origin_start);
46680 #ifdef MA_HAS_VORBIS
46681 if (result != MA_SUCCESS) {
46682 result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);
46683 if (result != MA_SUCCESS) {
46684 onSeek(pDecoder, 0, ma_seek_origin_start);
46689 if (result != MA_SUCCESS) {
46693 return ma_decoder__postinit(pConfig, pDecoder);
46696 MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
46698 ma_decoder_config config;
46701 config = ma_decoder_config_init_copy(pConfig);
46703 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
46704 if (result != MA_SUCCESS) {
46708 return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder);
46712 static size_t ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
46714 size_t bytesRemaining;
46716 MA_ASSERT(pDecoder->backend.memory.dataSize >= pDecoder->backend.memory.currentReadPos);
46718 bytesRemaining = pDecoder->backend.memory.dataSize - pDecoder->backend.memory.currentReadPos;
46719 if (bytesToRead > bytesRemaining) {
46720 bytesToRead = bytesRemaining;
46723 if (bytesToRead > 0) {
46724 MA_COPY_MEMORY(pBufferOut, pDecoder->backend.memory.pData + pDecoder->backend.memory.currentReadPos, bytesToRead);
46725 pDecoder->backend.memory.currentReadPos += bytesToRead;
46728 return bytesToRead;
46731 static ma_bool32 ma_decoder__on_seek_memory(ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin)
46733 if (origin == ma_seek_origin_current) {
46734 if (byteOffset > 0) {
46735 if (pDecoder->backend.memory.currentReadPos + byteOffset > pDecoder->backend.memory.dataSize) {
46736 byteOffset = (int)(pDecoder->backend.memory.dataSize - pDecoder->backend.memory.currentReadPos); /* Trying to seek too far forward. */
46739 if (pDecoder->backend.memory.currentReadPos < (size_t)-byteOffset) {
46740 byteOffset = -(int)pDecoder->backend.memory.currentReadPos; /* Trying to seek too far backwards. */
46744 /* This will never underflow thanks to the clamps above. */
46745 pDecoder->backend.memory.currentReadPos += byteOffset;
46747 if ((ma_uint32)byteOffset <= pDecoder->backend.memory.dataSize) {
46748 pDecoder->backend.memory.currentReadPos = byteOffset;
46750 pDecoder->backend.memory.currentReadPos = pDecoder->backend.memory.dataSize; /* Trying to seek too far forward. */
46757 static ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
46759 ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, pConfig, pDecoder);
46760 if (result != MA_SUCCESS) {
46764 if (pData == NULL || dataSize == 0) {
46765 return MA_INVALID_ARGS;
46768 pDecoder->backend.memory.pData = (const ma_uint8*)pData;
46769 pDecoder->backend.memory.dataSize = dataSize;
46770 pDecoder->backend.memory.currentReadPos = 0;
46776 MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
46778 ma_decoder_config config;
46781 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
46783 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
46784 if (result != MA_SUCCESS) {
46788 return ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder);
46791 MA_API ma_result ma_decoder_init_memory_wav(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
46794 ma_decoder_config config;
46797 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
46799 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
46800 if (result != MA_SUCCESS) {
46804 result = ma_decoder_init_wav__internal(&config, pDecoder);
46805 if (result != MA_SUCCESS) {
46809 return ma_decoder__postinit(&config, pDecoder);
46815 return MA_NO_BACKEND;
46819 MA_API ma_result ma_decoder_init_memory_flac(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
46822 ma_decoder_config config;
46825 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
46827 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
46828 if (result != MA_SUCCESS) {
46832 result = ma_decoder_init_flac__internal(&config, pDecoder);
46833 if (result != MA_SUCCESS) {
46837 return ma_decoder__postinit(&config, pDecoder);
46843 return MA_NO_BACKEND;
46847 MA_API ma_result ma_decoder_init_memory_mp3(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
46850 ma_decoder_config config;
46853 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
46855 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
46856 if (result != MA_SUCCESS) {
46860 result = ma_decoder_init_mp3__internal(&config, pDecoder);
46861 if (result != MA_SUCCESS) {
46865 return ma_decoder__postinit(&config, pDecoder);
46871 return MA_NO_BACKEND;
46875 MA_API ma_result ma_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
46877 #ifdef MA_HAS_VORBIS
46878 ma_decoder_config config;
46881 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
46883 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
46884 if (result != MA_SUCCESS) {
46888 result = ma_decoder_init_vorbis__internal(&config, pDecoder);
46889 if (result != MA_SUCCESS) {
46893 return ma_decoder__postinit(&config, pDecoder);
46899 return MA_NO_BACKEND;
46903 MA_API ma_result ma_decoder_init_memory_raw(const void* pData, size_t dataSize, const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder)
46905 ma_decoder_config config;
46908 config = ma_decoder_config_init_copy(pConfigOut); /* Make sure the config is not NULL. */
46910 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
46911 if (result != MA_SUCCESS) {
46915 result = ma_decoder_init_raw__internal(pConfigIn, &config, pDecoder);
46916 if (result != MA_SUCCESS) {
46920 return ma_decoder__postinit(&config, pDecoder);
46924 #if defined(MA_HAS_WAV) || \
46925 defined(MA_HAS_MP3) || \
46926 defined(MA_HAS_FLAC) || \
46927 defined(MA_HAS_VORBIS) || \
46928 defined(MA_HAS_OPUS)
46929 #define MA_HAS_PATH_API
46932 #if defined(MA_HAS_PATH_API)
46933 static const char* ma_path_file_name(const char* path)
46935 const char* fileName;
46937 if (path == NULL) {
46943 /* We just loop through the path until we find the last slash. */
46944 while (path[0] != '\0') {
46945 if (path[0] == '/' || path[0] == '\\') {
46952 /* At this point the file name is sitting on a slash, so just move forward. */
46953 while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
46960 static const wchar_t* ma_path_file_name_w(const wchar_t* path)
46962 const wchar_t* fileName;
46964 if (path == NULL) {
46970 /* We just loop through the path until we find the last slash. */
46971 while (path[0] != '\0') {
46972 if (path[0] == '/' || path[0] == '\\') {
46979 /* At this point the file name is sitting on a slash, so just move forward. */
46980 while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
46988 static const char* ma_path_extension(const char* path)
46990 const char* extension;
46991 const char* lastOccurance;
46993 if (path == NULL) {
46997 extension = ma_path_file_name(path);
46998 lastOccurance = NULL;
47000 /* Just find the last '.' and return. */
47001 while (extension[0] != '\0') {
47002 if (extension[0] == '.') {
47004 lastOccurance = extension;
47010 return (lastOccurance != NULL) ? lastOccurance : extension;
47013 static const wchar_t* ma_path_extension_w(const wchar_t* path)
47015 const wchar_t* extension;
47016 const wchar_t* lastOccurance;
47018 if (path == NULL) {
47022 extension = ma_path_file_name_w(path);
47023 lastOccurance = NULL;
47025 /* Just find the last '.' and return. */
47026 while (extension[0] != '\0') {
47027 if (extension[0] == '.') {
47029 lastOccurance = extension;
47035 return (lastOccurance != NULL) ? lastOccurance : extension;
47039 static ma_bool32 ma_path_extension_equal(const char* path, const char* extension)
47044 if (path == NULL || extension == NULL) {
47049 ext2 = ma_path_extension(path);
47051 #if defined(_MSC_VER) || defined(__DMC__)
47052 return _stricmp(ext1, ext2) == 0;
47054 return strcasecmp(ext1, ext2) == 0;
47058 static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension)
47060 const wchar_t* ext1;
47061 const wchar_t* ext2;
47063 if (path == NULL || extension == NULL) {
47068 ext2 = ma_path_extension_w(path);
47070 #if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)
47071 return _wcsicmp(ext1, ext2) == 0;
47074 I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This
47075 isn't the most efficient way to do it, but it should work OK.
47080 const wchar_t* pext1 = ext1;
47081 const wchar_t* pext2 = ext2;
47085 MA_ZERO_OBJECT(&mbs1);
47086 MA_ZERO_OBJECT(&mbs2);
47088 if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) {
47091 if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) {
47095 return strcasecmp(ext1MB, ext2MB) == 0;
47099 #endif /* MA_HAS_PATH_API */
47103 static size_t ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
47107 MA_ASSERT(pDecoder != NULL);
47108 MA_ASSERT(pBufferOut != NULL);
47110 ma_vfs_or_default_read(pDecoder->backend.vfs.pVFS, pDecoder->backend.vfs.file, pBufferOut, bytesToRead, &bytesRead);
47115 static ma_bool32 ma_decoder__on_seek_vfs(ma_decoder* pDecoder, int offset, ma_seek_origin origin)
47119 MA_ASSERT(pDecoder != NULL);
47121 result = ma_vfs_or_default_seek(pDecoder->backend.vfs.pVFS, pDecoder->backend.vfs.file, offset, origin);
47122 if (result != MA_SUCCESS) {
47129 static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47134 result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, pConfig, pDecoder);
47135 if (result != MA_SUCCESS) {
47139 if (pFilePath == NULL || pFilePath[0] == '\0') {
47140 return MA_INVALID_ARGS;
47143 result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
47144 if (result != MA_SUCCESS) {
47148 pDecoder->backend.vfs.pVFS = pVFS;
47149 pDecoder->backend.vfs.file = file;
47154 MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47157 ma_decoder_config config;
47159 config = ma_decoder_config_init_copy(pConfig);
47160 result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder);
47161 if (result != MA_SUCCESS) {
47165 result = MA_NO_BACKEND;
47168 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) {
47169 result = ma_decoder_init_wav__internal(&config, pDecoder);
47170 if (result != MA_SUCCESS) {
47171 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
47176 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) {
47177 result = ma_decoder_init_flac__internal(&config, pDecoder);
47178 if (result != MA_SUCCESS) {
47179 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
47184 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) {
47185 result = ma_decoder_init_mp3__internal(&config, pDecoder);
47186 if (result != MA_SUCCESS) {
47187 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
47192 /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */
47193 if (result != MA_SUCCESS) {
47194 result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder);
47196 result = ma_decoder__postinit(&config, pDecoder);
47199 if (result != MA_SUCCESS) {
47200 ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file);
47207 MA_API ma_result ma_decoder_init_vfs_wav(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47211 ma_decoder_config config;
47213 config = ma_decoder_config_init_copy(pConfig);
47214 result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder);
47215 if (result != MA_SUCCESS) {
47219 result = ma_decoder_init_wav__internal(&config, pDecoder);
47220 if (result == MA_SUCCESS) {
47221 result = ma_decoder__postinit(&config, pDecoder);
47224 if (result != MA_SUCCESS) {
47225 ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file);
47234 return MA_NO_BACKEND;
47238 MA_API ma_result ma_decoder_init_vfs_flac(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47242 ma_decoder_config config;
47244 config = ma_decoder_config_init_copy(pConfig);
47245 result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder);
47246 if (result != MA_SUCCESS) {
47250 result = ma_decoder_init_flac__internal(&config, pDecoder);
47251 if (result == MA_SUCCESS) {
47252 result = ma_decoder__postinit(&config, pDecoder);
47255 if (result != MA_SUCCESS) {
47256 ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file);
47265 return MA_NO_BACKEND;
47269 MA_API ma_result ma_decoder_init_vfs_mp3(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47273 ma_decoder_config config;
47275 config = ma_decoder_config_init_copy(pConfig);
47276 result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder);
47277 if (result != MA_SUCCESS) {
47281 result = ma_decoder_init_mp3__internal(&config, pDecoder);
47282 if (result == MA_SUCCESS) {
47283 result = ma_decoder__postinit(&config, pDecoder);
47286 if (result != MA_SUCCESS) {
47287 ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file);
47296 return MA_NO_BACKEND;
47300 MA_API ma_result ma_decoder_init_vfs_vorbis(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47302 #ifdef MA_HAS_VORBIS
47304 ma_decoder_config config;
47306 config = ma_decoder_config_init_copy(pConfig);
47307 result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder);
47308 if (result != MA_SUCCESS) {
47312 result = ma_decoder_init_vorbis__internal(&config, pDecoder);
47313 if (result == MA_SUCCESS) {
47314 result = ma_decoder__postinit(&config, pDecoder);
47317 if (result != MA_SUCCESS) {
47318 ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file);
47327 return MA_NO_BACKEND;
47333 static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47338 result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, pConfig, pDecoder);
47339 if (result != MA_SUCCESS) {
47343 if (pFilePath == NULL || pFilePath[0] == '\0') {
47344 return MA_INVALID_ARGS;
47347 result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
47348 if (result != MA_SUCCESS) {
47352 pDecoder->backend.vfs.pVFS = pVFS;
47353 pDecoder->backend.vfs.file = file;
47358 MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47361 ma_decoder_config config;
47363 config = ma_decoder_config_init_copy(pConfig);
47364 result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder);
47365 if (result != MA_SUCCESS) {
47369 result = MA_NO_BACKEND;
47372 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) {
47373 result = ma_decoder_init_wav__internal(&config, pDecoder);
47374 if (result != MA_SUCCESS) {
47375 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
47380 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) {
47381 result = ma_decoder_init_flac__internal(&config, pDecoder);
47382 if (result != MA_SUCCESS) {
47383 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
47388 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) {
47389 result = ma_decoder_init_mp3__internal(&config, pDecoder);
47390 if (result != MA_SUCCESS) {
47391 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
47396 /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */
47397 if (result != MA_SUCCESS) {
47398 result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder);
47400 result = ma_decoder__postinit(&config, pDecoder);
47403 if (result != MA_SUCCESS) {
47404 ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file);
47411 MA_API ma_result ma_decoder_init_vfs_wav_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47415 ma_decoder_config config;
47417 config = ma_decoder_config_init_copy(pConfig);
47418 result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder);
47419 if (result != MA_SUCCESS) {
47423 result = ma_decoder_init_wav__internal(&config, pDecoder);
47424 if (result == MA_SUCCESS) {
47425 result = ma_decoder__postinit(&config, pDecoder);
47428 if (result != MA_SUCCESS) {
47429 ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file);
47438 return MA_NO_BACKEND;
47442 MA_API ma_result ma_decoder_init_vfs_flac_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47446 ma_decoder_config config;
47448 config = ma_decoder_config_init_copy(pConfig);
47449 result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder);
47450 if (result != MA_SUCCESS) {
47454 result = ma_decoder_init_flac__internal(&config, pDecoder);
47455 if (result == MA_SUCCESS) {
47456 result = ma_decoder__postinit(&config, pDecoder);
47459 if (result != MA_SUCCESS) {
47460 ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file);
47469 return MA_NO_BACKEND;
47473 MA_API ma_result ma_decoder_init_vfs_mp3_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47477 ma_decoder_config config;
47479 config = ma_decoder_config_init_copy(pConfig);
47480 result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder);
47481 if (result != MA_SUCCESS) {
47485 result = ma_decoder_init_mp3__internal(&config, pDecoder);
47486 if (result == MA_SUCCESS) {
47487 result = ma_decoder__postinit(&config, pDecoder);
47490 if (result != MA_SUCCESS) {
47491 ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file);
47500 return MA_NO_BACKEND;
47504 MA_API ma_result ma_decoder_init_vfs_vorbis_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47506 #ifdef MA_HAS_VORBIS
47508 ma_decoder_config config;
47510 config = ma_decoder_config_init_copy(pConfig);
47511 result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder);
47512 if (result != MA_SUCCESS) {
47516 result = ma_decoder_init_vorbis__internal(&config, pDecoder);
47517 if (result == MA_SUCCESS) {
47518 result = ma_decoder__postinit(&config, pDecoder);
47521 if (result != MA_SUCCESS) {
47522 ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file);
47531 return MA_NO_BACKEND;
47537 MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47539 return ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder);
47542 MA_API ma_result ma_decoder_init_file_wav(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47544 return ma_decoder_init_vfs_wav(NULL, pFilePath, pConfig, pDecoder);
47547 MA_API ma_result ma_decoder_init_file_flac(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47549 return ma_decoder_init_vfs_flac(NULL, pFilePath, pConfig, pDecoder);
47552 MA_API ma_result ma_decoder_init_file_mp3(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47554 return ma_decoder_init_vfs_mp3(NULL, pFilePath, pConfig, pDecoder);
47557 MA_API ma_result ma_decoder_init_file_vorbis(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47559 return ma_decoder_init_vfs_vorbis(NULL, pFilePath, pConfig, pDecoder);
47564 MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47566 return ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder);
47569 MA_API ma_result ma_decoder_init_file_wav_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47571 return ma_decoder_init_vfs_wav_w(NULL, pFilePath, pConfig, pDecoder);
47574 MA_API ma_result ma_decoder_init_file_flac_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47576 return ma_decoder_init_vfs_flac_w(NULL, pFilePath, pConfig, pDecoder);
47579 MA_API ma_result ma_decoder_init_file_mp3_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47581 return ma_decoder_init_vfs_mp3_w(NULL, pFilePath, pConfig, pDecoder);
47584 MA_API ma_result ma_decoder_init_file_vorbis_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47586 return ma_decoder_init_vfs_vorbis_w(NULL, pFilePath, pConfig, pDecoder);
47589 MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder)
47591 if (pDecoder == NULL) {
47592 return MA_INVALID_ARGS;
47595 if (pDecoder->onUninit) {
47596 pDecoder->onUninit(pDecoder);
47599 if (pDecoder->onRead == ma_decoder__on_read_vfs) {
47600 ma_vfs_or_default_close(pDecoder->backend.vfs.pVFS, pDecoder->backend.vfs.file);
47603 ma_data_converter_uninit(&pDecoder->converter);
47608 MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor)
47610 if (pCursor == NULL) {
47611 return MA_INVALID_ARGS;
47616 if (pDecoder == NULL) {
47617 return MA_INVALID_ARGS;
47620 *pCursor = pDecoder->readPointerInPCMFrames;
47625 MA_API ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder)
47627 if (pDecoder == NULL) {
47631 if (pDecoder->onGetLengthInPCMFrames) {
47632 ma_uint64 nativeLengthInPCMFrames = pDecoder->onGetLengthInPCMFrames(pDecoder);
47633 if (pDecoder->internalSampleRate == pDecoder->outputSampleRate) {
47634 return nativeLengthInPCMFrames;
47636 return ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, pDecoder->internalSampleRate, nativeLengthInPCMFrames);
47643 MA_API ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
47646 ma_uint64 totalFramesReadOut;
47647 ma_uint64 totalFramesReadIn;
47648 void* pRunningFramesOut;
47650 if (pDecoder == NULL) {
47654 if (pDecoder->onReadPCMFrames == NULL) {
47659 if (pDecoder->converter.isPassthrough) {
47660 totalFramesReadOut = pDecoder->onReadPCMFrames(pDecoder, pFramesOut, frameCount);
47663 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
47664 need to run through each sample because we need to ensure it's internal cache is updated.
47666 if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) {
47667 totalFramesReadOut = pDecoder->onReadPCMFrames(pDecoder, NULL, frameCount); /* All decoder backends must support passing in NULL for the output buffer. */
47669 /* Slow path. Need to run everything through the data converter. */
47670 totalFramesReadOut = 0;
47671 totalFramesReadIn = 0;
47672 pRunningFramesOut = pFramesOut;
47674 while (totalFramesReadOut < frameCount) {
47675 ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */
47676 ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
47677 ma_uint64 framesToReadThisIterationIn;
47678 ma_uint64 framesReadThisIterationIn;
47679 ma_uint64 framesToReadThisIterationOut;
47680 ma_uint64 framesReadThisIterationOut;
47681 ma_uint64 requiredInputFrameCount;
47683 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
47684 framesToReadThisIterationIn = framesToReadThisIterationOut;
47685 if (framesToReadThisIterationIn > intermediaryBufferCap) {
47686 framesToReadThisIterationIn = intermediaryBufferCap;
47689 requiredInputFrameCount = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut);
47690 if (framesToReadThisIterationIn > requiredInputFrameCount) {
47691 framesToReadThisIterationIn = requiredInputFrameCount;
47694 if (requiredInputFrameCount > 0) {
47695 framesReadThisIterationIn = pDecoder->onReadPCMFrames(pDecoder, pIntermediaryBuffer, framesToReadThisIterationIn);
47696 totalFramesReadIn += framesReadThisIterationIn;
47698 framesReadThisIterationIn = 0;
47702 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
47703 input frames, we still want to try processing frames because there may some output frames generated from cached input data.
47705 framesReadThisIterationOut = framesToReadThisIterationOut;
47706 result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
47707 if (result != MA_SUCCESS) {
47711 totalFramesReadOut += framesReadThisIterationOut;
47713 if (pRunningFramesOut != NULL) {
47714 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
47717 if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
47718 break; /* We're done. */
47724 pDecoder->readPointerInPCMFrames += totalFramesReadOut;
47726 return totalFramesReadOut;
47729 MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex)
47731 if (pDecoder == NULL) {
47732 return MA_INVALID_ARGS;
47735 if (pDecoder->onSeekToPCMFrame) {
47737 ma_uint64 internalFrameIndex;
47738 if (pDecoder->internalSampleRate == pDecoder->outputSampleRate) {
47739 internalFrameIndex = frameIndex;
47741 internalFrameIndex = ma_calculate_frame_count_after_resampling(pDecoder->internalSampleRate, pDecoder->outputSampleRate, frameIndex);
47744 result = pDecoder->onSeekToPCMFrame(pDecoder, internalFrameIndex);
47745 if (result == MA_SUCCESS) {
47746 pDecoder->readPointerInPCMFrames = frameIndex;
47752 /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */
47753 return MA_INVALID_ARGS;
47756 MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames)
47758 ma_uint64 totalFrameCount;
47760 if (pAvailableFrames == NULL) {
47761 return MA_INVALID_ARGS;
47764 *pAvailableFrames = 0;
47766 if (pDecoder == NULL) {
47767 return MA_INVALID_ARGS;
47770 totalFrameCount = ma_decoder_get_length_in_pcm_frames(pDecoder);
47771 if (totalFrameCount == 0) {
47772 return MA_NOT_IMPLEMENTED;
47775 if (totalFrameCount <= pDecoder->readPointerInPCMFrames) {
47776 *pAvailableFrames = 0;
47778 *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames;
47781 return MA_SUCCESS; /* No frames available. */
47785 static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
47787 ma_uint64 totalFrameCount;
47789 ma_uint64 dataCapInFrames;
47790 void* pPCMFramesOut;
47792 MA_ASSERT(pDecoder != NULL);
47794 totalFrameCount = 0;
47795 bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
47797 /* The frame count is unknown until we try reading. Thus, we just run in a loop. */
47798 dataCapInFrames = 0;
47799 pPCMFramesOut = NULL;
47801 ma_uint64 frameCountToTryReading;
47802 ma_uint64 framesJustRead;
47804 /* Make room if there's not enough. */
47805 if (totalFrameCount == dataCapInFrames) {
47806 void* pNewPCMFramesOut;
47807 ma_uint64 oldDataCapInFrames = dataCapInFrames;
47808 ma_uint64 newDataCapInFrames = dataCapInFrames*2;
47809 if (newDataCapInFrames == 0) {
47810 newDataCapInFrames = 4096;
47813 if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) {
47814 ma__free_from_callbacks(pPCMFramesOut, &pDecoder->allocationCallbacks);
47819 pNewPCMFramesOut = (void*)ma__realloc_from_callbacks(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), (size_t)(oldDataCapInFrames * bpf), &pDecoder->allocationCallbacks);
47820 if (pNewPCMFramesOut == NULL) {
47821 ma__free_from_callbacks(pPCMFramesOut, &pDecoder->allocationCallbacks);
47822 return MA_OUT_OF_MEMORY;
47825 dataCapInFrames = newDataCapInFrames;
47826 pPCMFramesOut = pNewPCMFramesOut;
47829 frameCountToTryReading = dataCapInFrames - totalFrameCount;
47830 MA_ASSERT(frameCountToTryReading > 0);
47832 framesJustRead = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading);
47833 totalFrameCount += framesJustRead;
47835 if (framesJustRead < frameCountToTryReading) {
47841 if (pConfigOut != NULL) {
47842 pConfigOut->format = pDecoder->outputFormat;
47843 pConfigOut->channels = pDecoder->outputChannels;
47844 pConfigOut->sampleRate = pDecoder->outputSampleRate;
47845 ma_channel_map_copy(pConfigOut->channelMap, pDecoder->outputChannelMap, pDecoder->outputChannels);
47848 if (ppPCMFramesOut != NULL) {
47849 *ppPCMFramesOut = pPCMFramesOut;
47851 ma__free_from_callbacks(pPCMFramesOut, &pDecoder->allocationCallbacks);
47854 if (pFrameCountOut != NULL) {
47855 *pFrameCountOut = totalFrameCount;
47858 ma_decoder_uninit(pDecoder);
47862 MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
47865 ma_decoder_config config;
47866 ma_decoder decoder;
47868 if (pFrameCountOut != NULL) {
47869 *pFrameCountOut = 0;
47871 if (ppPCMFramesOut != NULL) {
47872 *ppPCMFramesOut = NULL;
47875 config = ma_decoder_config_init_copy(pConfig);
47877 result = ma_decoder_init_vfs(pVFS, pFilePath, &config, &decoder);
47878 if (result != MA_SUCCESS) {
47882 result = ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
47887 MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
47889 return ma_decode_from_vfs(NULL, pFilePath, pConfig, pFrameCountOut, ppPCMFramesOut);
47892 MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
47894 ma_decoder_config config;
47895 ma_decoder decoder;
47898 if (pFrameCountOut != NULL) {
47899 *pFrameCountOut = 0;
47901 if (ppPCMFramesOut != NULL) {
47902 *ppPCMFramesOut = NULL;
47905 if (pData == NULL || dataSize == 0) {
47906 return MA_INVALID_ARGS;
47909 config = ma_decoder_config_init_copy(pConfig);
47911 result = ma_decoder_init_memory(pData, dataSize, &config, &decoder);
47912 if (result != MA_SUCCESS) {
47916 return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
47918 #endif /* MA_NO_DECODING */
47921 #ifndef MA_NO_ENCODING
47923 #if defined(MA_HAS_WAV)
47924 static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite)
47926 ma_encoder* pEncoder = (ma_encoder*)pUserData;
47927 MA_ASSERT(pEncoder != NULL);
47929 return pEncoder->onWrite(pEncoder, pData, bytesToWrite);
47932 static drwav_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, drwav_seek_origin origin)
47934 ma_encoder* pEncoder = (ma_encoder*)pUserData;
47935 MA_ASSERT(pEncoder != NULL);
47937 return pEncoder->onSeek(pEncoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
47940 static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder)
47942 drwav_data_format wavFormat;
47943 drwav_allocation_callbacks allocationCallbacks;
47946 MA_ASSERT(pEncoder != NULL);
47948 pWav = (drwav*)ma__malloc_from_callbacks(sizeof(*pWav), &pEncoder->config.allocationCallbacks);
47949 if (pWav == NULL) {
47950 return MA_OUT_OF_MEMORY;
47953 wavFormat.container = drwav_container_riff;
47954 wavFormat.channels = pEncoder->config.channels;
47955 wavFormat.sampleRate = pEncoder->config.sampleRate;
47956 wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8;
47957 if (pEncoder->config.format == ma_format_f32) {
47958 wavFormat.format = DR_WAVE_FORMAT_IEEE_FLOAT;
47960 wavFormat.format = DR_WAVE_FORMAT_PCM;
47963 allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData;
47964 allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc;
47965 allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc;
47966 allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree;
47968 if (!drwav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) {
47972 pEncoder->pInternalEncoder = pWav;
47977 static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder)
47981 MA_ASSERT(pEncoder != NULL);
47983 pWav = (drwav*)pEncoder->pInternalEncoder;
47984 MA_ASSERT(pWav != NULL);
47986 drwav_uninit(pWav);
47987 ma__free_from_callbacks(pWav, &pEncoder->config.allocationCallbacks);
47990 static ma_uint64 ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount)
47994 MA_ASSERT(pEncoder != NULL);
47996 pWav = (drwav*)pEncoder->pInternalEncoder;
47997 MA_ASSERT(pWav != NULL);
47999 return drwav_write_pcm_frames(pWav, frameCount, pFramesIn);
48003 MA_API ma_encoder_config ma_encoder_config_init(ma_resource_format resourceFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
48005 ma_encoder_config config;
48007 MA_ZERO_OBJECT(&config);
48008 config.resourceFormat = resourceFormat;
48009 config.format = format;
48010 config.channels = channels;
48011 config.sampleRate = sampleRate;
48016 MA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder)
48020 if (pEncoder == NULL) {
48021 return MA_INVALID_ARGS;
48024 MA_ZERO_OBJECT(pEncoder);
48026 if (pConfig == NULL) {
48027 return MA_INVALID_ARGS;
48030 if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) {
48031 return MA_INVALID_ARGS;
48034 pEncoder->config = *pConfig;
48036 result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks);
48037 if (result != MA_SUCCESS) {
48044 MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder)
48046 ma_result result = MA_SUCCESS;
48048 /* This assumes ma_encoder_preinit() has been called prior. */
48049 MA_ASSERT(pEncoder != NULL);
48051 if (onWrite == NULL || onSeek == NULL) {
48052 return MA_INVALID_ARGS;
48055 pEncoder->onWrite = onWrite;
48056 pEncoder->onSeek = onSeek;
48057 pEncoder->pUserData = pUserData;
48059 switch (pEncoder->config.resourceFormat)
48061 case ma_resource_format_wav:
48063 #if defined(MA_HAS_WAV)
48064 pEncoder->onInit = ma_encoder__on_init_wav;
48065 pEncoder->onUninit = ma_encoder__on_uninit_wav;
48066 pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav;
48068 result = MA_NO_BACKEND;
48074 result = MA_INVALID_ARGS;
48078 /* Getting here means we should have our backend callbacks set up. */
48079 if (result == MA_SUCCESS) {
48080 result = pEncoder->onInit(pEncoder);
48081 if (result != MA_SUCCESS) {
48089 MA_API size_t ma_encoder__on_write_stdio(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite)
48091 return fwrite(pBufferIn, 1, bytesToWrite, (FILE*)pEncoder->pFile);
48094 MA_API ma_bool32 ma_encoder__on_seek_stdio(ma_encoder* pEncoder, int byteOffset, ma_seek_origin origin)
48096 return fseek((FILE*)pEncoder->pFile, byteOffset, (origin == ma_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
48099 MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
48104 result = ma_encoder_preinit(pConfig, pEncoder);
48105 if (result != MA_SUCCESS) {
48109 /* Now open the file. If this fails we don't need to uninitialize the encoder. */
48110 result = ma_fopen(&pFile, pFilePath, "wb");
48111 if (pFile == NULL) {
48115 pEncoder->pFile = pFile;
48117 return ma_encoder_init__internal(ma_encoder__on_write_stdio, ma_encoder__on_seek_stdio, NULL, pEncoder);
48120 MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
48125 result = ma_encoder_preinit(pConfig, pEncoder);
48126 if (result != MA_SUCCESS) {
48130 /* Now open the file. If this fails we don't need to uninitialize the encoder. */
48131 result = ma_wfopen(&pFile, pFilePath, L"wb", &pEncoder->config.allocationCallbacks);
48132 if (pFile != NULL) {
48136 pEncoder->pFile = pFile;
48138 return ma_encoder_init__internal(ma_encoder__on_write_stdio, ma_encoder__on_seek_stdio, NULL, pEncoder);
48141 MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
48145 result = ma_encoder_preinit(pConfig, pEncoder);
48146 if (result != MA_SUCCESS) {
48150 return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder);
48154 MA_API void ma_encoder_uninit(ma_encoder* pEncoder)
48156 if (pEncoder == NULL) {
48160 if (pEncoder->onUninit) {
48161 pEncoder->onUninit(pEncoder);
48164 /* If we have a file handle, close it. */
48165 if (pEncoder->onWrite == ma_encoder__on_write_stdio) {
48166 fclose((FILE*)pEncoder->pFile);
48171 MA_API ma_uint64 ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount)
48173 if (pEncoder == NULL || pFramesIn == NULL) {
48177 return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount);
48179 #endif /* MA_NO_ENCODING */
48183 /**************************************************************************************************************************************************************
48187 **************************************************************************************************************************************************************/
48188 #ifndef MA_NO_GENERATION
48189 MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency)
48191 ma_waveform_config config;
48193 MA_ZERO_OBJECT(&config);
48194 config.format = format;
48195 config.channels = channels;
48196 config.sampleRate = sampleRate;
48197 config.type = type;
48198 config.amplitude = amplitude;
48199 config.frequency = frequency;
48204 static ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
48206 ma_uint64 framesRead = ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount);
48208 if (pFramesRead != NULL) {
48209 *pFramesRead = framesRead;
48212 if (framesRead < frameCount) {
48219 static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
48221 return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex);
48224 static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
48226 ma_waveform* pWaveform = (ma_waveform*)pDataSource;
48228 *pFormat = pWaveform->config.format;
48229 *pChannels = pWaveform->config.channels;
48230 *pSampleRate = pWaveform->config.sampleRate;
48235 static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
48237 ma_waveform* pWaveform = (ma_waveform*)pDataSource;
48239 *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance);
48244 static double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency)
48246 return (1.0 / (sampleRate / frequency));
48249 static void ma_waveform__update_advance(ma_waveform* pWaveform)
48251 pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency);
48254 MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform)
48256 if (pWaveform == NULL) {
48257 return MA_INVALID_ARGS;
48260 MA_ZERO_OBJECT(pWaveform);
48261 pWaveform->ds.onRead = ma_waveform__data_source_on_read;
48262 pWaveform->ds.onSeek = ma_waveform__data_source_on_seek;
48263 pWaveform->ds.onGetDataFormat = ma_waveform__data_source_on_get_data_format;
48264 pWaveform->ds.onGetCursor = ma_waveform__data_source_on_get_cursor;
48265 pWaveform->ds.onGetLength = NULL; /* Intentionally set to NULL since there's no notion of a length in waveforms. */
48266 pWaveform->config = *pConfig;
48267 pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency);
48268 pWaveform->time = 0;
48273 MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude)
48275 if (pWaveform == NULL) {
48276 return MA_INVALID_ARGS;
48279 pWaveform->config.amplitude = amplitude;
48283 MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency)
48285 if (pWaveform == NULL) {
48286 return MA_INVALID_ARGS;
48289 pWaveform->config.frequency = frequency;
48290 ma_waveform__update_advance(pWaveform);
48295 MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type)
48297 if (pWaveform == NULL) {
48298 return MA_INVALID_ARGS;
48301 pWaveform->config.type = type;
48305 MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate)
48307 if (pWaveform == NULL) {
48308 return MA_INVALID_ARGS;
48311 pWaveform->config.sampleRate = sampleRate;
48312 ma_waveform__update_advance(pWaveform);
48317 static float ma_waveform_sine_f32(double time, double amplitude)
48319 return (float)(ma_sin(MA_TAU_D * time) * amplitude);
48322 static ma_int16 ma_waveform_sine_s16(double time, double amplitude)
48324 return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude));
48327 static float ma_waveform_square_f32(double time, double amplitude)
48329 double f = time - (ma_int64)time;
48341 static ma_int16 ma_waveform_square_s16(double time, double amplitude)
48343 return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, amplitude));
48346 static float ma_waveform_triangle_f32(double time, double amplitude)
48348 double f = time - (ma_int64)time;
48351 r = 2 * ma_abs(2 * (f - 0.5)) - 1;
48353 return (float)(r * amplitude);
48356 static ma_int16 ma_waveform_triangle_s16(double time, double amplitude)
48358 return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude));
48361 static float ma_waveform_sawtooth_f32(double time, double amplitude)
48363 double f = time - (ma_int64)time;
48368 return (float)(r * amplitude);
48371 static ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude)
48373 return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude));
48376 static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
48379 ma_uint64 iChannel;
48380 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
48381 ma_uint32 bpf = bps * pWaveform->config.channels;
48383 MA_ASSERT(pWaveform != NULL);
48384 MA_ASSERT(pFramesOut != NULL);
48386 if (pWaveform->config.format == ma_format_f32) {
48387 float* pFramesOutF32 = (float*)pFramesOut;
48388 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48389 float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude);
48390 pWaveform->time += pWaveform->advance;
48392 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
48393 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
48396 } else if (pWaveform->config.format == ma_format_s16) {
48397 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
48398 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48399 ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude);
48400 pWaveform->time += pWaveform->advance;
48402 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
48403 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
48407 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48408 float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude);
48409 pWaveform->time += pWaveform->advance;
48411 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
48412 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
48418 static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
48421 ma_uint64 iChannel;
48422 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
48423 ma_uint32 bpf = bps * pWaveform->config.channels;
48425 MA_ASSERT(pWaveform != NULL);
48426 MA_ASSERT(pFramesOut != NULL);
48428 if (pWaveform->config.format == ma_format_f32) {
48429 float* pFramesOutF32 = (float*)pFramesOut;
48430 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48431 float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude);
48432 pWaveform->time += pWaveform->advance;
48434 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
48435 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
48438 } else if (pWaveform->config.format == ma_format_s16) {
48439 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
48440 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48441 ma_int16 s = ma_waveform_square_s16(pWaveform->time, pWaveform->config.amplitude);
48442 pWaveform->time += pWaveform->advance;
48444 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
48445 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
48449 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48450 float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude);
48451 pWaveform->time += pWaveform->advance;
48453 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
48454 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
48460 static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
48463 ma_uint64 iChannel;
48464 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
48465 ma_uint32 bpf = bps * pWaveform->config.channels;
48467 MA_ASSERT(pWaveform != NULL);
48468 MA_ASSERT(pFramesOut != NULL);
48470 if (pWaveform->config.format == ma_format_f32) {
48471 float* pFramesOutF32 = (float*)pFramesOut;
48472 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48473 float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude);
48474 pWaveform->time += pWaveform->advance;
48476 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
48477 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
48480 } else if (pWaveform->config.format == ma_format_s16) {
48481 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
48482 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48483 ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude);
48484 pWaveform->time += pWaveform->advance;
48486 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
48487 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
48491 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48492 float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude);
48493 pWaveform->time += pWaveform->advance;
48495 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
48496 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
48502 static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
48505 ma_uint64 iChannel;
48506 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
48507 ma_uint32 bpf = bps * pWaveform->config.channels;
48509 MA_ASSERT(pWaveform != NULL);
48510 MA_ASSERT(pFramesOut != NULL);
48512 if (pWaveform->config.format == ma_format_f32) {
48513 float* pFramesOutF32 = (float*)pFramesOut;
48514 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48515 float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude);
48516 pWaveform->time += pWaveform->advance;
48518 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
48519 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
48522 } else if (pWaveform->config.format == ma_format_s16) {
48523 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
48524 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48525 ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude);
48526 pWaveform->time += pWaveform->advance;
48528 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
48529 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
48533 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48534 float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude);
48535 pWaveform->time += pWaveform->advance;
48537 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
48538 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
48544 MA_API ma_uint64 ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
48546 if (pWaveform == NULL) {
48550 if (pFramesOut != NULL) {
48551 switch (pWaveform->config.type)
48553 case ma_waveform_type_sine:
48555 ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount);
48558 case ma_waveform_type_square:
48560 ma_waveform_read_pcm_frames__square(pWaveform, pFramesOut, frameCount);
48563 case ma_waveform_type_triangle:
48565 ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount);
48568 case ma_waveform_type_sawtooth:
48570 ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount);
48576 pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */
48582 MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex)
48584 if (pWaveform == NULL) {
48585 return MA_INVALID_ARGS;
48588 pWaveform->time = pWaveform->advance * (ma_int64)frameIndex; /* Casting for VC6. Won't be an issue in practice. */
48594 MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude)
48596 ma_noise_config config;
48597 MA_ZERO_OBJECT(&config);
48599 config.format = format;
48600 config.channels = channels;
48601 config.type = type;
48602 config.seed = seed;
48603 config.amplitude = amplitude;
48605 if (config.seed == 0) {
48606 config.seed = MA_DEFAULT_LCG_SEED;
48613 static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
48615 ma_uint64 framesRead = ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount);
48617 if (pFramesRead != NULL) {
48618 *pFramesRead = framesRead;
48621 if (framesRead < frameCount) {
48628 static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
48630 /* No-op. Just pretend to be successful. */
48636 static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
48638 ma_noise* pNoise = (ma_noise*)pDataSource;
48640 *pFormat = pNoise->config.format;
48641 *pChannels = pNoise->config.channels;
48642 *pSampleRate = 0; /* There is no notion of sample rate with noise generation. */
48647 MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, ma_noise* pNoise)
48649 if (pNoise == NULL) {
48650 return MA_INVALID_ARGS;
48653 MA_ZERO_OBJECT(pNoise);
48655 if (pConfig == NULL) {
48656 return MA_INVALID_ARGS;
48659 if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) {
48660 return MA_INVALID_ARGS;
48663 pNoise->ds.onRead = ma_noise__data_source_on_read;
48664 pNoise->ds.onSeek = ma_noise__data_source_on_seek; /* <-- No-op for noise. */
48665 pNoise->ds.onGetDataFormat = ma_noise__data_source_on_get_data_format;
48666 pNoise->ds.onGetCursor = NULL; /* No notion of a cursor for noise. */
48667 pNoise->ds.onGetLength = NULL; /* No notion of a length for noise. */
48668 pNoise->config = *pConfig;
48669 ma_lcg_seed(&pNoise->lcg, pConfig->seed);
48671 if (pNoise->config.type == ma_noise_type_pink) {
48672 ma_uint32 iChannel;
48673 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
48674 pNoise->state.pink.accumulation[iChannel] = 0;
48675 pNoise->state.pink.counter[iChannel] = 1;
48679 if (pNoise->config.type == ma_noise_type_brownian) {
48680 ma_uint32 iChannel;
48681 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
48682 pNoise->state.brownian.accumulation[iChannel] = 0;
48689 MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude)
48691 if (pNoise == NULL) {
48692 return MA_INVALID_ARGS;
48695 pNoise->config.amplitude = amplitude;
48699 MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed)
48701 if (pNoise == NULL) {
48702 return MA_INVALID_ARGS;
48705 pNoise->lcg.state = seed;
48710 MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type)
48712 if (pNoise == NULL) {
48713 return MA_INVALID_ARGS;
48716 pNoise->config.type = type;
48720 static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise)
48722 return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude);
48725 static MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise)
48727 return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise));
48730 static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
48733 ma_uint32 iChannel;
48734 const ma_uint32 channels = pNoise->config.channels;
48735 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
48737 if (pNoise->config.format == ma_format_f32) {
48738 float* pFramesOutF32 = (float*)pFramesOut;
48739 if (pNoise->config.duplicateChannels) {
48740 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48741 float s = ma_noise_f32_white(pNoise);
48742 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48743 pFramesOutF32[iFrame*channels + iChannel] = s;
48747 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48748 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48749 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_white(pNoise);
48753 } else if (pNoise->config.format == ma_format_s16) {
48754 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
48755 if (pNoise->config.duplicateChannels) {
48756 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48757 ma_int16 s = ma_noise_s16_white(pNoise);
48758 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48759 pFramesOutS16[iFrame*channels + iChannel] = s;
48763 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48764 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48765 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_white(pNoise);
48770 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
48771 const ma_uint32 bpf = bps * channels;
48773 if (pNoise->config.duplicateChannels) {
48774 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48775 float s = ma_noise_f32_white(pNoise);
48776 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48777 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
48781 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48782 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48783 float s = ma_noise_f32_white(pNoise);
48784 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
48794 static MA_INLINE unsigned int ma_tzcnt32(unsigned int x)
48798 /* Special case for odd numbers since they should happen about half the time. */
48804 return sizeof(x) << 3;
48808 if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; }
48809 if ((x & 0x000000FF) == 0) { x >>= 8; n += 8; }
48810 if ((x & 0x0000000F) == 0) { x >>= 4; n += 4; }
48811 if ((x & 0x00000003) == 0) { x >>= 2; n += 2; }
48812 n -= x & 0x00000001;
48818 Pink noise generation based on Tonic (public domain) with modifications. https://github.com/TonicAudio/Tonic/blob/master/src/Tonic/Noise.h
48820 This is basically _the_ reference for pink noise from what I've found: http://www.firstpr.com.au/dsp/pink-noise/
48822 static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel)
48829 ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (ma_countof(pNoise->state.pink.bin[0]) - 1);
48831 binPrev = pNoise->state.pink.bin[iChannel][ibin];
48832 binNext = ma_lcg_rand_f64(&pNoise->lcg);
48833 pNoise->state.pink.bin[iChannel][ibin] = binNext;
48835 pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev);
48836 pNoise->state.pink.counter[iChannel] += 1;
48838 result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]);
48841 return (float)(result * pNoise->config.amplitude);
48844 static MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel)
48846 return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel));
48849 static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
48852 ma_uint32 iChannel;
48853 const ma_uint32 channels = pNoise->config.channels;
48854 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
48856 if (pNoise->config.format == ma_format_f32) {
48857 float* pFramesOutF32 = (float*)pFramesOut;
48858 if (pNoise->config.duplicateChannels) {
48859 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48860 float s = ma_noise_f32_pink(pNoise, 0);
48861 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48862 pFramesOutF32[iFrame*channels + iChannel] = s;
48866 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48867 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48868 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel);
48872 } else if (pNoise->config.format == ma_format_s16) {
48873 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
48874 if (pNoise->config.duplicateChannels) {
48875 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48876 ma_int16 s = ma_noise_s16_pink(pNoise, 0);
48877 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48878 pFramesOutS16[iFrame*channels + iChannel] = s;
48882 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48883 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48884 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel);
48889 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
48890 const ma_uint32 bpf = bps * channels;
48892 if (pNoise->config.duplicateChannels) {
48893 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48894 float s = ma_noise_f32_pink(pNoise, 0);
48895 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48896 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
48900 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48901 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48902 float s = ma_noise_f32_pink(pNoise, iChannel);
48903 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
48913 static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel)
48917 result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]);
48918 result /= 1.005; /* Don't escape the -1..1 range on average. */
48920 pNoise->state.brownian.accumulation[iChannel] = result;
48923 return (float)(result * pNoise->config.amplitude);
48926 static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel)
48928 return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel));
48931 static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
48934 ma_uint32 iChannel;
48935 const ma_uint32 channels = pNoise->config.channels;
48936 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
48938 if (pNoise->config.format == ma_format_f32) {
48939 float* pFramesOutF32 = (float*)pFramesOut;
48940 if (pNoise->config.duplicateChannels) {
48941 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48942 float s = ma_noise_f32_brownian(pNoise, 0);
48943 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48944 pFramesOutF32[iFrame*channels + iChannel] = s;
48948 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48949 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48950 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel);
48954 } else if (pNoise->config.format == ma_format_s16) {
48955 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
48956 if (pNoise->config.duplicateChannels) {
48957 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48958 ma_int16 s = ma_noise_s16_brownian(pNoise, 0);
48959 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48960 pFramesOutS16[iFrame*channels + iChannel] = s;
48964 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48965 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48966 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel);
48971 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
48972 const ma_uint32 bpf = bps * channels;
48974 if (pNoise->config.duplicateChannels) {
48975 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48976 float s = ma_noise_f32_brownian(pNoise, 0);
48977 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48978 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
48982 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48983 for (iChannel = 0; iChannel < channels; iChannel += 1) {
48984 float s = ma_noise_f32_brownian(pNoise, iChannel);
48985 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
48994 MA_API ma_uint64 ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
48996 if (pNoise == NULL) {
49000 /* 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. */
49001 if (pFramesOut == NULL) {
49005 if (pNoise->config.type == ma_noise_type_white) {
49006 return ma_noise_read_pcm_frames__white(pNoise, pFramesOut, frameCount);
49009 if (pNoise->config.type == ma_noise_type_pink) {
49010 return ma_noise_read_pcm_frames__pink(pNoise, pFramesOut, frameCount);
49013 if (pNoise->config.type == ma_noise_type_brownian) {
49014 return ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount);
49017 /* Should never get here. */
49018 MA_ASSERT(MA_FALSE);
49021 #endif /* MA_NO_GENERATION */
49025 /**************************************************************************************************************************************************************
49026 ***************************************************************************************************************************************************************
49030 All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as dr_wav, dr_flac, etc. If you find a bug in the
49031 code below please report the bug to the respective repository for the relevant project (probably dr_libs).
49033 ***************************************************************************************************************************************************************
49034 **************************************************************************************************************************************************************/
49035 #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))
49036 #if !defined(DR_WAV_IMPLEMENTATION) && !defined(DRWAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
49037 /* dr_wav_c begin */
49040 #include <stdlib.h>
49041 #include <string.h>
49042 #include <limits.h>
49043 #ifndef DR_WAV_NO_STDIO
49047 #ifndef DRWAV_ASSERT
49048 #include <assert.h>
49049 #define DRWAV_ASSERT(expression) assert(expression)
49051 #ifndef DRWAV_MALLOC
49052 #define DRWAV_MALLOC(sz) malloc((sz))
49054 #ifndef DRWAV_REALLOC
49055 #define DRWAV_REALLOC(p, sz) realloc((p), (sz))
49058 #define DRWAV_FREE(p) free((p))
49060 #ifndef DRWAV_COPY_MEMORY
49061 #define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
49063 #ifndef DRWAV_ZERO_MEMORY
49064 #define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
49066 #ifndef DRWAV_ZERO_OBJECT
49067 #define DRWAV_ZERO_OBJECT(p) DRWAV_ZERO_MEMORY((p), sizeof(*p))
49069 #define drwav_countof(x) (sizeof(x) / sizeof(x[0]))
49070 #define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
49071 #define drwav_min(a, b) (((a) < (b)) ? (a) : (b))
49072 #define drwav_max(a, b) (((a) > (b)) ? (a) : (b))
49073 #define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x))))
49074 #define DRWAV_MAX_SIMD_VECTOR_SIZE 64
49075 #if defined(__x86_64__) || defined(_M_X64)
49077 #elif defined(__i386) || defined(_M_IX86)
49079 #elif defined(__arm__) || defined(_M_ARM)
49083 #define DRWAV_INLINE __forceinline
49084 #elif defined(__GNUC__)
49085 #if defined(__STRICT_ANSI__)
49086 #define DRWAV_INLINE __inline__ __attribute__((always_inline))
49088 #define DRWAV_INLINE inline __attribute__((always_inline))
49090 #elif defined(__WATCOMC__)
49091 #define DRWAV_INLINE __inline
49093 #define DRWAV_INLINE
49095 #if defined(SIZE_MAX)
49096 #define DRWAV_SIZE_MAX SIZE_MAX
49098 #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
49099 #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF)
49101 #define DRWAV_SIZE_MAX 0xFFFFFFFF
49104 #if defined(_MSC_VER) && _MSC_VER >= 1400
49105 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
49106 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
49107 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
49108 #elif defined(__clang__)
49109 #if defined(__has_builtin)
49110 #if __has_builtin(__builtin_bswap16)
49111 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
49113 #if __has_builtin(__builtin_bswap32)
49114 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
49116 #if __has_builtin(__builtin_bswap64)
49117 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
49120 #elif defined(__GNUC__)
49121 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
49122 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
49123 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
49125 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
49126 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
49129 DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision)
49132 *pMajor = DRWAV_VERSION_MAJOR;
49135 *pMinor = DRWAV_VERSION_MINOR;
49138 *pRevision = DRWAV_VERSION_REVISION;
49141 DRWAV_API const char* drwav_version_string(void)
49143 return DRWAV_VERSION_STRING;
49145 #ifndef DRWAV_MAX_SAMPLE_RATE
49146 #define DRWAV_MAX_SAMPLE_RATE 384000
49148 #ifndef DRWAV_MAX_CHANNELS
49149 #define DRWAV_MAX_CHANNELS 256
49151 #ifndef DRWAV_MAX_BITS_PER_SAMPLE
49152 #define DRWAV_MAX_BITS_PER_SAMPLE 64
49154 static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00};
49155 static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
49156 static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
49157 static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
49158 static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
49159 static const drwav_uint8 drwavGUID_W64_SMPL[16] = {0x73,0x6D,0x70,0x6C, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
49160 static DRWAV_INLINE int drwav__is_little_endian(void)
49162 #if defined(DRWAV_X86) || defined(DRWAV_X64)
49164 #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
49168 return (*(char*)&n) == 1;
49171 static DRWAV_INLINE void drwav__bytes_to_guid(const drwav_uint8* data, drwav_uint8* guid)
49174 for (i = 0; i < 16; ++i) {
49178 static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n)
49180 #ifdef DRWAV_HAS_BYTESWAP16_INTRINSIC
49181 #if defined(_MSC_VER)
49182 return _byteswap_ushort(n);
49183 #elif defined(__GNUC__) || defined(__clang__)
49184 return __builtin_bswap16(n);
49186 #error "This compiler does not support the byte swap intrinsic."
49189 return ((n & 0xFF00) >> 8) |
49190 ((n & 0x00FF) << 8);
49193 static DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n)
49195 #ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC
49196 #if defined(_MSC_VER)
49197 return _byteswap_ulong(n);
49198 #elif defined(__GNUC__) || defined(__clang__)
49199 #if defined(DRWAV_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRWAV_64BIT)
49201 __asm__ __volatile__ (
49202 #if defined(DRWAV_64BIT)
49203 "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n)
49205 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
49210 return __builtin_bswap32(n);
49213 #error "This compiler does not support the byte swap intrinsic."
49216 return ((n & 0xFF000000) >> 24) |
49217 ((n & 0x00FF0000) >> 8) |
49218 ((n & 0x0000FF00) << 8) |
49219 ((n & 0x000000FF) << 24);
49222 static DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n)
49224 #ifdef DRWAV_HAS_BYTESWAP64_INTRINSIC
49225 #if defined(_MSC_VER)
49226 return _byteswap_uint64(n);
49227 #elif defined(__GNUC__) || defined(__clang__)
49228 return __builtin_bswap64(n);
49230 #error "This compiler does not support the byte swap intrinsic."
49233 return ((n & ((drwav_uint64)0xFF000000 << 32)) >> 56) |
49234 ((n & ((drwav_uint64)0x00FF0000 << 32)) >> 40) |
49235 ((n & ((drwav_uint64)0x0000FF00 << 32)) >> 24) |
49236 ((n & ((drwav_uint64)0x000000FF << 32)) >> 8) |
49237 ((n & ((drwav_uint64)0xFF000000 )) << 8) |
49238 ((n & ((drwav_uint64)0x00FF0000 )) << 24) |
49239 ((n & ((drwav_uint64)0x0000FF00 )) << 40) |
49240 ((n & ((drwav_uint64)0x000000FF )) << 56);
49243 static DRWAV_INLINE drwav_int16 drwav__bswap_s16(drwav_int16 n)
49245 return (drwav_int16)drwav__bswap16((drwav_uint16)n);
49247 static DRWAV_INLINE void drwav__bswap_samples_s16(drwav_int16* pSamples, drwav_uint64 sampleCount)
49249 drwav_uint64 iSample;
49250 for (iSample = 0; iSample < sampleCount; iSample += 1) {
49251 pSamples[iSample] = drwav__bswap_s16(pSamples[iSample]);
49254 static DRWAV_INLINE void drwav__bswap_s24(drwav_uint8* p)
49261 static DRWAV_INLINE void drwav__bswap_samples_s24(drwav_uint8* pSamples, drwav_uint64 sampleCount)
49263 drwav_uint64 iSample;
49264 for (iSample = 0; iSample < sampleCount; iSample += 1) {
49265 drwav_uint8* pSample = pSamples + (iSample*3);
49266 drwav__bswap_s24(pSample);
49269 static DRWAV_INLINE drwav_int32 drwav__bswap_s32(drwav_int32 n)
49271 return (drwav_int32)drwav__bswap32((drwav_uint32)n);
49273 static DRWAV_INLINE void drwav__bswap_samples_s32(drwav_int32* pSamples, drwav_uint64 sampleCount)
49275 drwav_uint64 iSample;
49276 for (iSample = 0; iSample < sampleCount; iSample += 1) {
49277 pSamples[iSample] = drwav__bswap_s32(pSamples[iSample]);
49280 static DRWAV_INLINE float drwav__bswap_f32(float n)
49287 x.i = drwav__bswap32(x.i);
49290 static DRWAV_INLINE void drwav__bswap_samples_f32(float* pSamples, drwav_uint64 sampleCount)
49292 drwav_uint64 iSample;
49293 for (iSample = 0; iSample < sampleCount; iSample += 1) {
49294 pSamples[iSample] = drwav__bswap_f32(pSamples[iSample]);
49297 static DRWAV_INLINE double drwav__bswap_f64(double n)
49304 x.i = drwav__bswap64(x.i);
49307 static DRWAV_INLINE void drwav__bswap_samples_f64(double* pSamples, drwav_uint64 sampleCount)
49309 drwav_uint64 iSample;
49310 for (iSample = 0; iSample < sampleCount; iSample += 1) {
49311 pSamples[iSample] = drwav__bswap_f64(pSamples[iSample]);
49314 static DRWAV_INLINE void drwav__bswap_samples_pcm(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample)
49316 switch (bytesPerSample)
49320 drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount);
49324 drwav__bswap_samples_s24((drwav_uint8*)pSamples, sampleCount);
49328 drwav__bswap_samples_s32((drwav_int32*)pSamples, sampleCount);
49332 DRWAV_ASSERT(DRWAV_FALSE);
49336 static DRWAV_INLINE void drwav__bswap_samples_ieee(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample)
49338 switch (bytesPerSample)
49343 drwav__bswap_samples_f16((drwav_float16*)pSamples, sampleCount);
49348 drwav__bswap_samples_f32((float*)pSamples, sampleCount);
49352 drwav__bswap_samples_f64((double*)pSamples, sampleCount);
49356 DRWAV_ASSERT(DRWAV_FALSE);
49360 static DRWAV_INLINE void drwav__bswap_samples(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample, drwav_uint16 format)
49364 case DR_WAVE_FORMAT_PCM:
49366 drwav__bswap_samples_pcm(pSamples, sampleCount, bytesPerSample);
49368 case DR_WAVE_FORMAT_IEEE_FLOAT:
49370 drwav__bswap_samples_ieee(pSamples, sampleCount, bytesPerSample);
49372 case DR_WAVE_FORMAT_ALAW:
49373 case DR_WAVE_FORMAT_MULAW:
49375 drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount);
49377 case DR_WAVE_FORMAT_ADPCM:
49378 case DR_WAVE_FORMAT_DVI_ADPCM:
49381 DRWAV_ASSERT(DRWAV_FALSE);
49385 DRWAV_PRIVATE void* drwav__malloc_default(size_t sz, void* pUserData)
49388 return DRWAV_MALLOC(sz);
49390 DRWAV_PRIVATE void* drwav__realloc_default(void* p, size_t sz, void* pUserData)
49393 return DRWAV_REALLOC(p, sz);
49395 DRWAV_PRIVATE void drwav__free_default(void* p, void* pUserData)
49400 DRWAV_PRIVATE void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocation_callbacks* pAllocationCallbacks)
49402 if (pAllocationCallbacks == NULL) {
49405 if (pAllocationCallbacks->onMalloc != NULL) {
49406 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
49408 if (pAllocationCallbacks->onRealloc != NULL) {
49409 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
49413 DRWAV_PRIVATE void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drwav_allocation_callbacks* pAllocationCallbacks)
49415 if (pAllocationCallbacks == NULL) {
49418 if (pAllocationCallbacks->onRealloc != NULL) {
49419 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
49421 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
49423 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
49428 DRWAV_COPY_MEMORY(p2, p, szOld);
49429 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
49435 DRWAV_PRIVATE void drwav__free_from_callbacks(void* p, const drwav_allocation_callbacks* pAllocationCallbacks)
49437 if (p == NULL || pAllocationCallbacks == NULL) {
49440 if (pAllocationCallbacks->onFree != NULL) {
49441 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
49444 DRWAV_PRIVATE drwav_allocation_callbacks drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks* pAllocationCallbacks)
49446 if (pAllocationCallbacks != NULL) {
49447 return *pAllocationCallbacks;
49449 drwav_allocation_callbacks allocationCallbacks;
49450 allocationCallbacks.pUserData = NULL;
49451 allocationCallbacks.onMalloc = drwav__malloc_default;
49452 allocationCallbacks.onRealloc = drwav__realloc_default;
49453 allocationCallbacks.onFree = drwav__free_default;
49454 return allocationCallbacks;
49457 static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag)
49460 formatTag == DR_WAVE_FORMAT_ADPCM ||
49461 formatTag == DR_WAVE_FORMAT_DVI_ADPCM;
49463 DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize)
49465 return (unsigned int)(chunkSize % 2);
49467 DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize)
49469 return (unsigned int)(chunkSize % 8);
49471 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
49472 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
49473 DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
49474 DRWAV_PRIVATE drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut)
49476 if (container == drwav_container_riff || container == drwav_container_rf64) {
49477 drwav_uint8 sizeInBytes[4];
49478 if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) {
49479 return DRWAV_AT_END;
49481 if (onRead(pUserData, sizeInBytes, 4) != 4) {
49482 return DRWAV_INVALID_FILE;
49484 pHeaderOut->sizeInBytes = drwav_bytes_to_u32(sizeInBytes);
49485 pHeaderOut->paddingSize = drwav__chunk_padding_size_riff(pHeaderOut->sizeInBytes);
49486 *pRunningBytesReadOut += 8;
49488 drwav_uint8 sizeInBytes[8];
49489 if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) {
49490 return DRWAV_AT_END;
49492 if (onRead(pUserData, sizeInBytes, 8) != 8) {
49493 return DRWAV_INVALID_FILE;
49495 pHeaderOut->sizeInBytes = drwav_bytes_to_u64(sizeInBytes) - 24;
49496 pHeaderOut->paddingSize = drwav__chunk_padding_size_w64(pHeaderOut->sizeInBytes);
49497 *pRunningBytesReadOut += 24;
49499 return DRWAV_SUCCESS;
49501 DRWAV_PRIVATE drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
49503 drwav_uint64 bytesRemainingToSeek = offset;
49504 while (bytesRemainingToSeek > 0) {
49505 if (bytesRemainingToSeek > 0x7FFFFFFF) {
49506 if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
49507 return DRWAV_FALSE;
49509 bytesRemainingToSeek -= 0x7FFFFFFF;
49511 if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) {
49512 return DRWAV_FALSE;
49514 bytesRemainingToSeek = 0;
49519 DRWAV_PRIVATE drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
49521 if (offset <= 0x7FFFFFFF) {
49522 return onSeek(pUserData, (int)offset, drwav_seek_origin_start);
49524 if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) {
49525 return DRWAV_FALSE;
49527 offset -= 0x7FFFFFFF;
49529 if (offset <= 0x7FFFFFFF) {
49530 return onSeek(pUserData, (int)offset, drwav_seek_origin_current);
49532 if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
49533 return DRWAV_FALSE;
49535 offset -= 0x7FFFFFFF;
49538 DRWAV_PRIVATE drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut)
49540 drwav_chunk_header header;
49541 drwav_uint8 fmt[16];
49542 if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) {
49543 return DRWAV_FALSE;
49545 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))) {
49546 if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) {
49547 return DRWAV_FALSE;
49549 *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize;
49550 if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) {
49551 return DRWAV_FALSE;
49554 if (container == drwav_container_riff || container == drwav_container_rf64) {
49555 if (!drwav_fourcc_equal(header.id.fourcc, "fmt ")) {
49556 return DRWAV_FALSE;
49559 if (!drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT)) {
49560 return DRWAV_FALSE;
49563 if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) {
49564 return DRWAV_FALSE;
49566 *pRunningBytesReadOut += sizeof(fmt);
49567 fmtOut->formatTag = drwav_bytes_to_u16(fmt + 0);
49568 fmtOut->channels = drwav_bytes_to_u16(fmt + 2);
49569 fmtOut->sampleRate = drwav_bytes_to_u32(fmt + 4);
49570 fmtOut->avgBytesPerSec = drwav_bytes_to_u32(fmt + 8);
49571 fmtOut->blockAlign = drwav_bytes_to_u16(fmt + 12);
49572 fmtOut->bitsPerSample = drwav_bytes_to_u16(fmt + 14);
49573 fmtOut->extendedSize = 0;
49574 fmtOut->validBitsPerSample = 0;
49575 fmtOut->channelMask = 0;
49576 memset(fmtOut->subFormat, 0, sizeof(fmtOut->subFormat));
49577 if (header.sizeInBytes > 16) {
49578 drwav_uint8 fmt_cbSize[2];
49579 int bytesReadSoFar = 0;
49580 if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) {
49581 return DRWAV_FALSE;
49583 *pRunningBytesReadOut += sizeof(fmt_cbSize);
49584 bytesReadSoFar = 18;
49585 fmtOut->extendedSize = drwav_bytes_to_u16(fmt_cbSize);
49586 if (fmtOut->extendedSize > 0) {
49587 if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
49588 if (fmtOut->extendedSize != 22) {
49589 return DRWAV_FALSE;
49592 if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
49593 drwav_uint8 fmtext[22];
49594 if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) {
49595 return DRWAV_FALSE;
49597 fmtOut->validBitsPerSample = drwav_bytes_to_u16(fmtext + 0);
49598 fmtOut->channelMask = drwav_bytes_to_u32(fmtext + 2);
49599 drwav__bytes_to_guid(fmtext + 6, fmtOut->subFormat);
49601 if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) {
49602 return DRWAV_FALSE;
49605 *pRunningBytesReadOut += fmtOut->extendedSize;
49606 bytesReadSoFar += fmtOut->extendedSize;
49608 if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) {
49609 return DRWAV_FALSE;
49611 *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar);
49613 if (header.paddingSize > 0) {
49614 if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) {
49615 return DRWAV_FALSE;
49617 *pRunningBytesReadOut += header.paddingSize;
49621 DRWAV_PRIVATE size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor)
49624 DRWAV_ASSERT(onRead != NULL);
49625 DRWAV_ASSERT(pCursor != NULL);
49626 bytesRead = onRead(pUserData, pBufferOut, bytesToRead);
49627 *pCursor += bytesRead;
49631 DRWAV_PRIVATE drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor)
49633 DRWAV_ASSERT(onSeek != NULL);
49634 DRWAV_ASSERT(pCursor != NULL);
49635 if (!onSeek(pUserData, offset, origin)) {
49636 return DRWAV_FALSE;
49638 if (origin == drwav_seek_origin_start) {
49641 *pCursor += offset;
49646 DRWAV_PRIVATE drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav)
49648 if ((pWav->bitsPerSample & 0x7) == 0) {
49649 return (pWav->bitsPerSample * pWav->fmt.channels) >> 3;
49651 return pWav->fmt.blockAlign;
49654 DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT)
49656 if (pFMT == NULL) {
49659 if (pFMT->formatTag != DR_WAVE_FORMAT_EXTENSIBLE) {
49660 return pFMT->formatTag;
49662 return drwav_bytes_to_u16(pFMT->subFormat);
49665 DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
49667 if (pWav == NULL || onRead == NULL || onSeek == NULL) {
49668 return DRWAV_FALSE;
49670 DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));
49671 pWav->onRead = onRead;
49672 pWav->onSeek = onSeek;
49673 pWav->pUserData = pReadSeekUserData;
49674 pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
49675 if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
49676 return DRWAV_FALSE;
49680 DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags)
49682 drwav_uint64 cursor;
49683 drwav_bool32 sequential;
49684 drwav_uint8 riff[4];
49686 unsigned short translatedFormatTag;
49687 drwav_bool32 foundDataChunk;
49688 drwav_uint64 dataChunkSize = 0;
49689 drwav_uint64 sampleCountFromFactChunk = 0;
49690 drwav_uint64 chunkSize;
49692 sequential = (flags & DRWAV_SEQUENTIAL) != 0;
49693 if (drwav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) {
49694 return DRWAV_FALSE;
49696 if (drwav_fourcc_equal(riff, "RIFF")) {
49697 pWav->container = drwav_container_riff;
49698 } else if (drwav_fourcc_equal(riff, "riff")) {
49700 drwav_uint8 riff2[12];
49701 pWav->container = drwav_container_w64;
49702 if (drwav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) {
49703 return DRWAV_FALSE;
49705 for (i = 0; i < 12; ++i) {
49706 if (riff2[i] != drwavGUID_W64_RIFF[i+4]) {
49707 return DRWAV_FALSE;
49710 } else if (drwav_fourcc_equal(riff, "RF64")) {
49711 pWav->container = drwav_container_rf64;
49713 return DRWAV_FALSE;
49715 if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) {
49716 drwav_uint8 chunkSizeBytes[4];
49717 drwav_uint8 wave[4];
49718 if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
49719 return DRWAV_FALSE;
49721 if (pWav->container == drwav_container_riff) {
49722 if (drwav_bytes_to_u32(chunkSizeBytes) < 36) {
49723 return DRWAV_FALSE;
49726 if (drwav_bytes_to_u32(chunkSizeBytes) != 0xFFFFFFFF) {
49727 return DRWAV_FALSE;
49730 if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
49731 return DRWAV_FALSE;
49733 if (!drwav_fourcc_equal(wave, "WAVE")) {
49734 return DRWAV_FALSE;
49737 drwav_uint8 chunkSizeBytes[8];
49738 drwav_uint8 wave[16];
49739 if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
49740 return DRWAV_FALSE;
49742 if (drwav_bytes_to_u64(chunkSizeBytes) < 80) {
49743 return DRWAV_FALSE;
49745 if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
49746 return DRWAV_FALSE;
49748 if (!drwav_guid_equal(wave, drwavGUID_W64_WAVE)) {
49749 return DRWAV_FALSE;
49752 if (pWav->container == drwav_container_rf64) {
49753 drwav_uint8 sizeBytes[8];
49754 drwav_uint64 bytesRemainingInChunk;
49755 drwav_chunk_header header;
49756 drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
49757 if (result != DRWAV_SUCCESS) {
49758 return DRWAV_FALSE;
49760 if (!drwav_fourcc_equal(header.id.fourcc, "ds64")) {
49761 return DRWAV_FALSE;
49763 bytesRemainingInChunk = header.sizeInBytes + header.paddingSize;
49764 if (!drwav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) {
49765 return DRWAV_FALSE;
49767 bytesRemainingInChunk -= 8;
49769 if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
49770 return DRWAV_FALSE;
49772 bytesRemainingInChunk -= 8;
49773 dataChunkSize = drwav_bytes_to_u64(sizeBytes);
49774 if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
49775 return DRWAV_FALSE;
49777 bytesRemainingInChunk -= 8;
49778 sampleCountFromFactChunk = drwav_bytes_to_u64(sizeBytes);
49779 if (!drwav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) {
49780 return DRWAV_FALSE;
49782 cursor += bytesRemainingInChunk;
49784 if (!drwav__read_fmt(pWav->onRead, pWav->onSeek, pWav->pUserData, pWav->container, &cursor, &fmt)) {
49785 return DRWAV_FALSE;
49787 if ((fmt.sampleRate == 0 || fmt.sampleRate > DRWAV_MAX_SAMPLE_RATE) ||
49788 (fmt.channels == 0 || fmt.channels > DRWAV_MAX_CHANNELS) ||
49789 (fmt.bitsPerSample == 0 || fmt.bitsPerSample > DRWAV_MAX_BITS_PER_SAMPLE) ||
49790 fmt.blockAlign == 0) {
49791 return DRWAV_FALSE;
49793 translatedFormatTag = fmt.formatTag;
49794 if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
49795 translatedFormatTag = drwav_bytes_to_u16(fmt.subFormat + 0);
49797 foundDataChunk = DRWAV_FALSE;
49800 drwav_chunk_header header;
49801 drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
49802 if (result != DRWAV_SUCCESS) {
49803 if (!foundDataChunk) {
49804 return DRWAV_FALSE;
49809 if (!sequential && onChunk != NULL) {
49810 drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt);
49811 if (callbackBytesRead > 0) {
49812 if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) {
49813 return DRWAV_FALSE;
49817 if (!foundDataChunk) {
49818 pWav->dataChunkDataPos = cursor;
49820 chunkSize = header.sizeInBytes;
49821 if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) {
49822 if (drwav_fourcc_equal(header.id.fourcc, "data")) {
49823 foundDataChunk = DRWAV_TRUE;
49824 if (pWav->container != drwav_container_rf64) {
49825 dataChunkSize = chunkSize;
49829 if (drwav_guid_equal(header.id.guid, drwavGUID_W64_DATA)) {
49830 foundDataChunk = DRWAV_TRUE;
49831 dataChunkSize = chunkSize;
49834 if (foundDataChunk && sequential) {
49837 if (pWav->container == drwav_container_riff) {
49838 if (drwav_fourcc_equal(header.id.fourcc, "fact")) {
49839 drwav_uint32 sampleCount;
49840 if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) {
49841 return DRWAV_FALSE;
49844 if (!foundDataChunk) {
49845 pWav->dataChunkDataPos = cursor;
49847 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
49848 sampleCountFromFactChunk = sampleCount;
49850 sampleCountFromFactChunk = 0;
49853 } else if (pWav->container == drwav_container_w64) {
49854 if (drwav_guid_equal(header.id.guid, drwavGUID_W64_FACT)) {
49855 if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) {
49856 return DRWAV_FALSE;
49859 if (!foundDataChunk) {
49860 pWav->dataChunkDataPos = cursor;
49863 } else if (pWav->container == drwav_container_rf64) {
49865 if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) {
49866 if (drwav_fourcc_equal(header.id.fourcc, "smpl")) {
49867 drwav_uint8 smplHeaderData[36];
49868 if (chunkSize >= sizeof(smplHeaderData)) {
49869 drwav_uint64 bytesJustRead = drwav__on_read(pWav->onRead, pWav->pUserData, smplHeaderData, sizeof(smplHeaderData), &cursor);
49870 chunkSize -= bytesJustRead;
49871 if (bytesJustRead == sizeof(smplHeaderData)) {
49872 drwav_uint32 iLoop;
49873 pWav->smpl.manufacturer = drwav_bytes_to_u32(smplHeaderData+0);
49874 pWav->smpl.product = drwav_bytes_to_u32(smplHeaderData+4);
49875 pWav->smpl.samplePeriod = drwav_bytes_to_u32(smplHeaderData+8);
49876 pWav->smpl.midiUnityNotes = drwav_bytes_to_u32(smplHeaderData+12);
49877 pWav->smpl.midiPitchFraction = drwav_bytes_to_u32(smplHeaderData+16);
49878 pWav->smpl.smpteFormat = drwav_bytes_to_u32(smplHeaderData+20);
49879 pWav->smpl.smpteOffset = drwav_bytes_to_u32(smplHeaderData+24);
49880 pWav->smpl.numSampleLoops = drwav_bytes_to_u32(smplHeaderData+28);
49881 pWav->smpl.samplerData = drwav_bytes_to_u32(smplHeaderData+32);
49882 for (iLoop = 0; iLoop < pWav->smpl.numSampleLoops && iLoop < drwav_countof(pWav->smpl.loops); ++iLoop) {
49883 drwav_uint8 smplLoopData[24];
49884 bytesJustRead = drwav__on_read(pWav->onRead, pWav->pUserData, smplLoopData, sizeof(smplLoopData), &cursor);
49885 chunkSize -= bytesJustRead;
49886 if (bytesJustRead == sizeof(smplLoopData)) {
49887 pWav->smpl.loops[iLoop].cuePointId = drwav_bytes_to_u32(smplLoopData+0);
49888 pWav->smpl.loops[iLoop].type = drwav_bytes_to_u32(smplLoopData+4);
49889 pWav->smpl.loops[iLoop].start = drwav_bytes_to_u32(smplLoopData+8);
49890 pWav->smpl.loops[iLoop].end = drwav_bytes_to_u32(smplLoopData+12);
49891 pWav->smpl.loops[iLoop].fraction = drwav_bytes_to_u32(smplLoopData+16);
49892 pWav->smpl.loops[iLoop].playCount = drwav_bytes_to_u32(smplLoopData+20);
49902 if (drwav_guid_equal(header.id.guid, drwavGUID_W64_SMPL)) {
49905 chunkSize += header.paddingSize;
49906 if (!drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData)) {
49909 cursor += chunkSize;
49910 if (!foundDataChunk) {
49911 pWav->dataChunkDataPos = cursor;
49914 if (!foundDataChunk) {
49915 return DRWAV_FALSE;
49918 if (!drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) {
49919 return DRWAV_FALSE;
49921 cursor = pWav->dataChunkDataPos;
49924 pWav->sampleRate = fmt.sampleRate;
49925 pWav->channels = fmt.channels;
49926 pWav->bitsPerSample = fmt.bitsPerSample;
49927 pWav->bytesRemaining = dataChunkSize;
49928 pWav->translatedFormatTag = translatedFormatTag;
49929 pWav->dataChunkDataSize = dataChunkSize;
49930 if (sampleCountFromFactChunk != 0) {
49931 pWav->totalPCMFrameCount = sampleCountFromFactChunk;
49933 pWav->totalPCMFrameCount = dataChunkSize / drwav_get_bytes_per_pcm_frame(pWav);
49934 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
49935 drwav_uint64 totalBlockHeaderSizeInBytes;
49936 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
49937 if ((blockCount * fmt.blockAlign) < dataChunkSize) {
49940 totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels);
49941 pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
49943 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
49944 drwav_uint64 totalBlockHeaderSizeInBytes;
49945 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
49946 if ((blockCount * fmt.blockAlign) < dataChunkSize) {
49949 totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels);
49950 pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
49951 pWav->totalPCMFrameCount += blockCount;
49954 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
49955 if (pWav->channels > 2) {
49956 return DRWAV_FALSE;
49959 #ifdef DR_WAV_LIBSNDFILE_COMPAT
49960 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
49961 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
49962 pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels;
49964 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
49965 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
49966 pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels;
49971 DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
49973 return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks);
49975 DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
49977 if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) {
49978 return DRWAV_FALSE;
49980 return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
49982 DRWAV_PRIVATE drwav_uint32 drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize)
49984 drwav_uint64 chunkSize = 4 + 24 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize);
49985 if (chunkSize > 0xFFFFFFFFUL) {
49986 chunkSize = 0xFFFFFFFFUL;
49988 return (drwav_uint32)chunkSize;
49990 DRWAV_PRIVATE drwav_uint32 drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize)
49992 if (dataChunkSize <= 0xFFFFFFFFUL) {
49993 return (drwav_uint32)dataChunkSize;
49995 return 0xFFFFFFFFUL;
49998 DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize)
50000 drwav_uint64 dataSubchunkPaddingSize = drwav__chunk_padding_size_w64(dataChunkSize);
50001 return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize;
50003 DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize)
50005 return 24 + dataChunkSize;
50007 DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_rf64(drwav_uint64 dataChunkSize)
50009 drwav_uint64 chunkSize = 4 + 36 + 24 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize);
50010 if (chunkSize > 0xFFFFFFFFUL) {
50011 chunkSize = 0xFFFFFFFFUL;
50015 DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_rf64(drwav_uint64 dataChunkSize)
50017 return dataChunkSize;
50019 DRWAV_PRIVATE size_t drwav__write(drwav* pWav, const void* pData, size_t dataSize)
50021 DRWAV_ASSERT(pWav != NULL);
50022 DRWAV_ASSERT(pWav->onWrite != NULL);
50023 return pWav->onWrite(pWav->pUserData, pData, dataSize);
50025 DRWAV_PRIVATE size_t drwav__write_u16ne_to_le(drwav* pWav, drwav_uint16 value)
50027 DRWAV_ASSERT(pWav != NULL);
50028 DRWAV_ASSERT(pWav->onWrite != NULL);
50029 if (!drwav__is_little_endian()) {
50030 value = drwav__bswap16(value);
50032 return drwav__write(pWav, &value, 2);
50034 DRWAV_PRIVATE size_t drwav__write_u32ne_to_le(drwav* pWav, drwav_uint32 value)
50036 DRWAV_ASSERT(pWav != NULL);
50037 DRWAV_ASSERT(pWav->onWrite != NULL);
50038 if (!drwav__is_little_endian()) {
50039 value = drwav__bswap32(value);
50041 return drwav__write(pWav, &value, 4);
50043 DRWAV_PRIVATE size_t drwav__write_u64ne_to_le(drwav* pWav, drwav_uint64 value)
50045 DRWAV_ASSERT(pWav != NULL);
50046 DRWAV_ASSERT(pWav->onWrite != NULL);
50047 if (!drwav__is_little_endian()) {
50048 value = drwav__bswap64(value);
50050 return drwav__write(pWav, &value, 8);
50052 DRWAV_PRIVATE drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
50054 if (pWav == NULL || onWrite == NULL) {
50055 return DRWAV_FALSE;
50057 if (!isSequential && onSeek == NULL) {
50058 return DRWAV_FALSE;
50060 if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) {
50061 return DRWAV_FALSE;
50063 if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) {
50064 return DRWAV_FALSE;
50066 DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));
50067 pWav->onWrite = onWrite;
50068 pWav->onSeek = onSeek;
50069 pWav->pUserData = pUserData;
50070 pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
50071 if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
50072 return DRWAV_FALSE;
50074 pWav->fmt.formatTag = (drwav_uint16)pFormat->format;
50075 pWav->fmt.channels = (drwav_uint16)pFormat->channels;
50076 pWav->fmt.sampleRate = pFormat->sampleRate;
50077 pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8);
50078 pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8);
50079 pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
50080 pWav->fmt.extendedSize = 0;
50081 pWav->isSequentialWrite = isSequential;
50084 DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)
50086 size_t runningPos = 0;
50087 drwav_uint64 initialDataChunkSize = 0;
50088 drwav_uint64 chunkSizeFMT;
50089 if (pWav->isSequentialWrite) {
50090 initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8;
50091 if (pFormat->container == drwav_container_riff) {
50092 if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) {
50093 return DRWAV_FALSE;
50097 pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize;
50098 if (pFormat->container == drwav_container_riff) {
50099 drwav_uint32 chunkSizeRIFF = 28 + (drwav_uint32)initialDataChunkSize;
50100 runningPos += drwav__write(pWav, "RIFF", 4);
50101 runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF);
50102 runningPos += drwav__write(pWav, "WAVE", 4);
50103 } else if (pFormat->container == drwav_container_w64) {
50104 drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize;
50105 runningPos += drwav__write(pWav, drwavGUID_W64_RIFF, 16);
50106 runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeRIFF);
50107 runningPos += drwav__write(pWav, drwavGUID_W64_WAVE, 16);
50108 } else if (pFormat->container == drwav_container_rf64) {
50109 runningPos += drwav__write(pWav, "RF64", 4);
50110 runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF);
50111 runningPos += drwav__write(pWav, "WAVE", 4);
50113 if (pFormat->container == drwav_container_rf64) {
50114 drwav_uint32 initialds64ChunkSize = 28;
50115 drwav_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize;
50116 runningPos += drwav__write(pWav, "ds64", 4);
50117 runningPos += drwav__write_u32ne_to_le(pWav, initialds64ChunkSize);
50118 runningPos += drwav__write_u64ne_to_le(pWav, initialRiffChunkSize);
50119 runningPos += drwav__write_u64ne_to_le(pWav, initialDataChunkSize);
50120 runningPos += drwav__write_u64ne_to_le(pWav, totalSampleCount);
50121 runningPos += drwav__write_u32ne_to_le(pWav, 0);
50123 if (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64) {
50125 runningPos += drwav__write(pWav, "fmt ", 4);
50126 runningPos += drwav__write_u32ne_to_le(pWav, (drwav_uint32)chunkSizeFMT);
50127 } else if (pFormat->container == drwav_container_w64) {
50129 runningPos += drwav__write(pWav, drwavGUID_W64_FMT, 16);
50130 runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeFMT);
50132 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.formatTag);
50133 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.channels);
50134 runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate);
50135 runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec);
50136 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign);
50137 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample);
50138 if (pFormat->container == drwav_container_riff) {
50139 drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize;
50140 runningPos += drwav__write(pWav, "data", 4);
50141 runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeDATA);
50142 } else if (pFormat->container == drwav_container_w64) {
50143 drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize;
50144 runningPos += drwav__write(pWav, drwavGUID_W64_DATA, 16);
50145 runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeDATA);
50146 } else if (pFormat->container == drwav_container_rf64) {
50147 runningPos += drwav__write(pWav, "data", 4);
50148 runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF);
50150 pWav->container = pFormat->container;
50151 pWav->channels = (drwav_uint16)pFormat->channels;
50152 pWav->sampleRate = pFormat->sampleRate;
50153 pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
50154 pWav->translatedFormatTag = (drwav_uint16)pFormat->format;
50155 pWav->dataChunkDataPos = runningPos;
50158 DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
50160 if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
50161 return DRWAV_FALSE;
50163 return drwav_init_write__internal(pWav, pFormat, 0);
50165 DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
50167 if (!drwav_preinit_write(pWav, pFormat, DRWAV_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) {
50168 return DRWAV_FALSE;
50170 return drwav_init_write__internal(pWav, pFormat, totalSampleCount);
50172 DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
50174 if (pFormat == NULL) {
50175 return DRWAV_FALSE;
50177 return drwav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks);
50179 DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)
50181 drwav_uint64 targetDataSizeBytes = (drwav_uint64)((drwav_int64)totalSampleCount * pFormat->channels * pFormat->bitsPerSample/8.0);
50182 drwav_uint64 riffChunkSizeBytes;
50183 drwav_uint64 fileSizeBytes = 0;
50184 if (pFormat->container == drwav_container_riff) {
50185 riffChunkSizeBytes = drwav__riff_chunk_size_riff(targetDataSizeBytes);
50186 fileSizeBytes = (8 + riffChunkSizeBytes);
50187 } else if (pFormat->container == drwav_container_w64) {
50188 riffChunkSizeBytes = drwav__riff_chunk_size_w64(targetDataSizeBytes);
50189 fileSizeBytes = riffChunkSizeBytes;
50190 } else if (pFormat->container == drwav_container_rf64) {
50191 riffChunkSizeBytes = drwav__riff_chunk_size_rf64(targetDataSizeBytes);
50192 fileSizeBytes = (8 + riffChunkSizeBytes);
50194 return fileSizeBytes;
50196 #ifndef DR_WAV_NO_STDIO
50198 DRWAV_PRIVATE drwav_result drwav_result_from_errno(int e)
50202 case 0: return DRWAV_SUCCESS;
50204 case EPERM: return DRWAV_INVALID_OPERATION;
50207 case ENOENT: return DRWAV_DOES_NOT_EXIST;
50210 case ESRCH: return DRWAV_DOES_NOT_EXIST;
50213 case EINTR: return DRWAV_INTERRUPT;
50216 case EIO: return DRWAV_IO_ERROR;
50219 case ENXIO: return DRWAV_DOES_NOT_EXIST;
50222 case E2BIG: return DRWAV_INVALID_ARGS;
50225 case ENOEXEC: return DRWAV_INVALID_FILE;
50228 case EBADF: return DRWAV_INVALID_FILE;
50231 case ECHILD: return DRWAV_ERROR;
50234 case EAGAIN: return DRWAV_UNAVAILABLE;
50237 case ENOMEM: return DRWAV_OUT_OF_MEMORY;
50240 case EACCES: return DRWAV_ACCESS_DENIED;
50243 case EFAULT: return DRWAV_BAD_ADDRESS;
50246 case ENOTBLK: return DRWAV_ERROR;
50249 case EBUSY: return DRWAV_BUSY;
50252 case EEXIST: return DRWAV_ALREADY_EXISTS;
50255 case EXDEV: return DRWAV_ERROR;
50258 case ENODEV: return DRWAV_DOES_NOT_EXIST;
50261 case ENOTDIR: return DRWAV_NOT_DIRECTORY;
50264 case EISDIR: return DRWAV_IS_DIRECTORY;
50267 case EINVAL: return DRWAV_INVALID_ARGS;
50270 case ENFILE: return DRWAV_TOO_MANY_OPEN_FILES;
50273 case EMFILE: return DRWAV_TOO_MANY_OPEN_FILES;
50276 case ENOTTY: return DRWAV_INVALID_OPERATION;
50279 case ETXTBSY: return DRWAV_BUSY;
50282 case EFBIG: return DRWAV_TOO_BIG;
50285 case ENOSPC: return DRWAV_NO_SPACE;
50288 case ESPIPE: return DRWAV_BAD_SEEK;
50291 case EROFS: return DRWAV_ACCESS_DENIED;
50294 case EMLINK: return DRWAV_TOO_MANY_LINKS;
50297 case EPIPE: return DRWAV_BAD_PIPE;
50300 case EDOM: return DRWAV_OUT_OF_RANGE;
50303 case ERANGE: return DRWAV_OUT_OF_RANGE;
50306 case EDEADLK: return DRWAV_DEADLOCK;
50308 #ifdef ENAMETOOLONG
50309 case ENAMETOOLONG: return DRWAV_PATH_TOO_LONG;
50312 case ENOLCK: return DRWAV_ERROR;
50315 case ENOSYS: return DRWAV_NOT_IMPLEMENTED;
50318 case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY;
50321 case ELOOP: return DRWAV_TOO_MANY_LINKS;
50324 case ENOMSG: return DRWAV_NO_MESSAGE;
50327 case EIDRM: return DRWAV_ERROR;
50330 case ECHRNG: return DRWAV_ERROR;
50333 case EL2NSYNC: return DRWAV_ERROR;
50336 case EL3HLT: return DRWAV_ERROR;
50339 case EL3RST: return DRWAV_ERROR;
50342 case ELNRNG: return DRWAV_OUT_OF_RANGE;
50345 case EUNATCH: return DRWAV_ERROR;
50348 case ENOCSI: return DRWAV_ERROR;
50351 case EL2HLT: return DRWAV_ERROR;
50354 case EBADE: return DRWAV_ERROR;
50357 case EBADR: return DRWAV_ERROR;
50360 case EXFULL: return DRWAV_ERROR;
50363 case ENOANO: return DRWAV_ERROR;
50366 case EBADRQC: return DRWAV_ERROR;
50369 case EBADSLT: return DRWAV_ERROR;
50372 case EBFONT: return DRWAV_INVALID_FILE;
50375 case ENOSTR: return DRWAV_ERROR;
50378 case ENODATA: return DRWAV_NO_DATA_AVAILABLE;
50381 case ETIME: return DRWAV_TIMEOUT;
50384 case ENOSR: return DRWAV_NO_DATA_AVAILABLE;
50387 case ENONET: return DRWAV_NO_NETWORK;
50390 case ENOPKG: return DRWAV_ERROR;
50393 case EREMOTE: return DRWAV_ERROR;
50396 case ENOLINK: return DRWAV_ERROR;
50399 case EADV: return DRWAV_ERROR;
50402 case ESRMNT: return DRWAV_ERROR;
50405 case ECOMM: return DRWAV_ERROR;
50408 case EPROTO: return DRWAV_ERROR;
50411 case EMULTIHOP: return DRWAV_ERROR;
50414 case EDOTDOT: return DRWAV_ERROR;
50417 case EBADMSG: return DRWAV_BAD_MESSAGE;
50420 case EOVERFLOW: return DRWAV_TOO_BIG;
50423 case ENOTUNIQ: return DRWAV_NOT_UNIQUE;
50426 case EBADFD: return DRWAV_ERROR;
50429 case EREMCHG: return DRWAV_ERROR;
50432 case ELIBACC: return DRWAV_ACCESS_DENIED;
50435 case ELIBBAD: return DRWAV_INVALID_FILE;
50438 case ELIBSCN: return DRWAV_INVALID_FILE;
50441 case ELIBMAX: return DRWAV_ERROR;
50444 case ELIBEXEC: return DRWAV_ERROR;
50447 case EILSEQ: return DRWAV_INVALID_DATA;
50450 case ERESTART: return DRWAV_ERROR;
50453 case ESTRPIPE: return DRWAV_ERROR;
50456 case EUSERS: return DRWAV_ERROR;
50459 case ENOTSOCK: return DRWAV_NOT_SOCKET;
50461 #ifdef EDESTADDRREQ
50462 case EDESTADDRREQ: return DRWAV_NO_ADDRESS;
50465 case EMSGSIZE: return DRWAV_TOO_BIG;
50468 case EPROTOTYPE: return DRWAV_BAD_PROTOCOL;
50471 case ENOPROTOOPT: return DRWAV_PROTOCOL_UNAVAILABLE;
50473 #ifdef EPROTONOSUPPORT
50474 case EPROTONOSUPPORT: return DRWAV_PROTOCOL_NOT_SUPPORTED;
50476 #ifdef ESOCKTNOSUPPORT
50477 case ESOCKTNOSUPPORT: return DRWAV_SOCKET_NOT_SUPPORTED;
50480 case EOPNOTSUPP: return DRWAV_INVALID_OPERATION;
50482 #ifdef EPFNOSUPPORT
50483 case EPFNOSUPPORT: return DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED;
50485 #ifdef EAFNOSUPPORT
50486 case EAFNOSUPPORT: return DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED;
50489 case EADDRINUSE: return DRWAV_ALREADY_IN_USE;
50491 #ifdef EADDRNOTAVAIL
50492 case EADDRNOTAVAIL: return DRWAV_ERROR;
50495 case ENETDOWN: return DRWAV_NO_NETWORK;
50498 case ENETUNREACH: return DRWAV_NO_NETWORK;
50501 case ENETRESET: return DRWAV_NO_NETWORK;
50503 #ifdef ECONNABORTED
50504 case ECONNABORTED: return DRWAV_NO_NETWORK;
50507 case ECONNRESET: return DRWAV_CONNECTION_RESET;
50510 case ENOBUFS: return DRWAV_NO_SPACE;
50513 case EISCONN: return DRWAV_ALREADY_CONNECTED;
50516 case ENOTCONN: return DRWAV_NOT_CONNECTED;
50519 case ESHUTDOWN: return DRWAV_ERROR;
50521 #ifdef ETOOMANYREFS
50522 case ETOOMANYREFS: return DRWAV_ERROR;
50525 case ETIMEDOUT: return DRWAV_TIMEOUT;
50527 #ifdef ECONNREFUSED
50528 case ECONNREFUSED: return DRWAV_CONNECTION_REFUSED;
50531 case EHOSTDOWN: return DRWAV_NO_HOST;
50533 #ifdef EHOSTUNREACH
50534 case EHOSTUNREACH: return DRWAV_NO_HOST;
50537 case EALREADY: return DRWAV_IN_PROGRESS;
50540 case EINPROGRESS: return DRWAV_IN_PROGRESS;
50543 case ESTALE: return DRWAV_INVALID_FILE;
50546 case EUCLEAN: return DRWAV_ERROR;
50549 case ENOTNAM: return DRWAV_ERROR;
50552 case ENAVAIL: return DRWAV_ERROR;
50555 case EISNAM: return DRWAV_ERROR;
50558 case EREMOTEIO: return DRWAV_IO_ERROR;
50561 case EDQUOT: return DRWAV_NO_SPACE;
50564 case ENOMEDIUM: return DRWAV_DOES_NOT_EXIST;
50567 case EMEDIUMTYPE: return DRWAV_ERROR;
50570 case ECANCELED: return DRWAV_CANCELLED;
50573 case ENOKEY: return DRWAV_ERROR;
50576 case EKEYEXPIRED: return DRWAV_ERROR;
50579 case EKEYREVOKED: return DRWAV_ERROR;
50581 #ifdef EKEYREJECTED
50582 case EKEYREJECTED: return DRWAV_ERROR;
50585 case EOWNERDEAD: return DRWAV_ERROR;
50587 #ifdef ENOTRECOVERABLE
50588 case ENOTRECOVERABLE: return DRWAV_ERROR;
50591 case ERFKILL: return DRWAV_ERROR;
50594 case EHWPOISON: return DRWAV_ERROR;
50596 default: return DRWAV_ERROR;
50599 DRWAV_PRIVATE drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
50601 #if defined(_MSC_VER) && _MSC_VER >= 1400
50604 if (ppFile != NULL) {
50607 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
50608 return DRWAV_INVALID_ARGS;
50610 #if defined(_MSC_VER) && _MSC_VER >= 1400
50611 err = fopen_s(ppFile, pFilePath, pOpenMode);
50613 return drwav_result_from_errno(err);
50616 #if defined(_WIN32) || defined(__APPLE__)
50617 *ppFile = fopen(pFilePath, pOpenMode);
50619 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
50620 *ppFile = fopen64(pFilePath, pOpenMode);
50622 *ppFile = fopen(pFilePath, pOpenMode);
50625 if (*ppFile == NULL) {
50626 drwav_result result = drwav_result_from_errno(errno);
50627 if (result == DRWAV_SUCCESS) {
50628 result = DRWAV_ERROR;
50633 return DRWAV_SUCCESS;
50635 #if defined(_WIN32)
50636 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
50637 #define DRWAV_HAS_WFOPEN
50640 DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks)
50642 if (ppFile != NULL) {
50645 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
50646 return DRWAV_INVALID_ARGS;
50648 #if defined(DRWAV_HAS_WFOPEN)
50650 #if defined(_MSC_VER) && _MSC_VER >= 1400
50651 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
50653 return drwav_result_from_errno(err);
50656 *ppFile = _wfopen(pFilePath, pOpenMode);
50657 if (*ppFile == NULL) {
50658 return drwav_result_from_errno(errno);
50661 (void)pAllocationCallbacks;
50667 const wchar_t* pFilePathTemp = pFilePath;
50668 char* pFilePathMB = NULL;
50669 char pOpenModeMB[32] = {0};
50670 DRWAV_ZERO_OBJECT(&mbs);
50671 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
50672 if (lenMB == (size_t)-1) {
50673 return drwav_result_from_errno(errno);
50675 pFilePathMB = (char*)drwav__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);
50676 if (pFilePathMB == NULL) {
50677 return DRWAV_OUT_OF_MEMORY;
50679 pFilePathTemp = pFilePath;
50680 DRWAV_ZERO_OBJECT(&mbs);
50681 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
50685 if (pOpenMode[i] == 0) {
50686 pOpenModeMB[i] = '\0';
50689 pOpenModeMB[i] = (char)pOpenMode[i];
50693 *ppFile = fopen(pFilePathMB, pOpenModeMB);
50694 drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
50696 if (*ppFile == NULL) {
50697 return DRWAV_ERROR;
50700 return DRWAV_SUCCESS;
50702 DRWAV_PRIVATE size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
50704 return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
50706 DRWAV_PRIVATE size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite)
50708 return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData);
50710 DRWAV_PRIVATE drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin)
50712 return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
50714 DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks)
50716 return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
50718 DRWAV_PRIVATE drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFile, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
50720 drwav_bool32 result;
50721 result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
50722 if (result != DRWAV_TRUE) {
50726 result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
50727 if (result != DRWAV_TRUE) {
50733 DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
50736 if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) {
50737 return DRWAV_FALSE;
50739 return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);
50741 DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks)
50743 return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
50745 DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
50748 if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) {
50749 return DRWAV_FALSE;
50751 return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);
50753 DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
50755 drwav_bool32 result;
50756 result = drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
50757 if (result != DRWAV_TRUE) {
50761 result = drwav_init_write__internal(pWav, pFormat, totalSampleCount);
50762 if (result != DRWAV_TRUE) {
50768 DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
50771 if (drwav_fopen(&pFile, filename, "wb") != DRWAV_SUCCESS) {
50772 return DRWAV_FALSE;
50774 return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
50776 DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
50779 if (drwav_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != DRWAV_SUCCESS) {
50780 return DRWAV_FALSE;
50782 return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
50784 DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
50786 return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
50788 DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
50790 return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
50792 DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
50794 if (pFormat == NULL) {
50795 return DRWAV_FALSE;
50797 return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
50799 DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
50801 return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
50803 DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
50805 return drwav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
50807 DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
50809 if (pFormat == NULL) {
50810 return DRWAV_FALSE;
50812 return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
50815 DRWAV_PRIVATE size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
50817 drwav* pWav = (drwav*)pUserData;
50818 size_t bytesRemaining;
50819 DRWAV_ASSERT(pWav != NULL);
50820 DRWAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos);
50821 bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos;
50822 if (bytesToRead > bytesRemaining) {
50823 bytesToRead = bytesRemaining;
50825 if (bytesToRead > 0) {
50826 DRWAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead);
50827 pWav->memoryStream.currentReadPos += bytesToRead;
50829 return bytesToRead;
50831 DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin)
50833 drwav* pWav = (drwav*)pUserData;
50834 DRWAV_ASSERT(pWav != NULL);
50835 if (origin == drwav_seek_origin_current) {
50837 if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) {
50838 return DRWAV_FALSE;
50841 if (pWav->memoryStream.currentReadPos < (size_t)-offset) {
50842 return DRWAV_FALSE;
50845 pWav->memoryStream.currentReadPos += offset;
50847 if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) {
50848 pWav->memoryStream.currentReadPos = offset;
50850 return DRWAV_FALSE;
50855 DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite)
50857 drwav* pWav = (drwav*)pUserData;
50858 size_t bytesRemaining;
50859 DRWAV_ASSERT(pWav != NULL);
50860 DRWAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos);
50861 bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos;
50862 if (bytesRemaining < bytesToWrite) {
50864 size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2;
50865 if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) {
50866 newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite;
50868 pNewData = drwav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks);
50869 if (pNewData == NULL) {
50872 *pWav->memoryStreamWrite.ppData = pNewData;
50873 pWav->memoryStreamWrite.dataCapacity = newDataCapacity;
50875 DRWAV_COPY_MEMORY(((drwav_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite);
50876 pWav->memoryStreamWrite.currentWritePos += bytesToWrite;
50877 if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) {
50878 pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos;
50880 *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize;
50881 return bytesToWrite;
50883 DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin)
50885 drwav* pWav = (drwav*)pUserData;
50886 DRWAV_ASSERT(pWav != NULL);
50887 if (origin == drwav_seek_origin_current) {
50889 if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) {
50890 offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos);
50893 if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) {
50894 offset = -(int)pWav->memoryStreamWrite.currentWritePos;
50897 pWav->memoryStreamWrite.currentWritePos += offset;
50899 if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) {
50900 pWav->memoryStreamWrite.currentWritePos = offset;
50902 pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize;
50907 DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks)
50909 return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks);
50911 DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
50913 if (data == NULL || dataSize == 0) {
50914 return DRWAV_FALSE;
50916 if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) {
50917 return DRWAV_FALSE;
50919 pWav->memoryStream.data = (const drwav_uint8*)data;
50920 pWav->memoryStream.dataSize = dataSize;
50921 pWav->memoryStream.currentReadPos = 0;
50922 return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
50924 DRWAV_PRIVATE drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
50926 if (ppData == NULL || pDataSize == NULL) {
50927 return DRWAV_FALSE;
50931 if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, pWav, pAllocationCallbacks)) {
50932 return DRWAV_FALSE;
50934 pWav->memoryStreamWrite.ppData = ppData;
50935 pWav->memoryStreamWrite.pDataSize = pDataSize;
50936 pWav->memoryStreamWrite.dataSize = 0;
50937 pWav->memoryStreamWrite.dataCapacity = 0;
50938 pWav->memoryStreamWrite.currentWritePos = 0;
50939 return drwav_init_write__internal(pWav, pFormat, totalSampleCount);
50941 DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
50943 return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
50945 DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
50947 return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
50949 DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
50951 if (pFormat == NULL) {
50952 return DRWAV_FALSE;
50954 return drwav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
50956 DRWAV_API drwav_result drwav_uninit(drwav* pWav)
50958 drwav_result result = DRWAV_SUCCESS;
50959 if (pWav == NULL) {
50960 return DRWAV_INVALID_ARGS;
50962 if (pWav->onWrite != NULL) {
50963 drwav_uint32 paddingSize = 0;
50964 if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) {
50965 paddingSize = drwav__chunk_padding_size_riff(pWav->dataChunkDataSize);
50967 paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize);
50969 if (paddingSize > 0) {
50970 drwav_uint64 paddingData = 0;
50971 drwav__write(pWav, &paddingData, paddingSize);
50973 if (pWav->onSeek && !pWav->isSequentialWrite) {
50974 if (pWav->container == drwav_container_riff) {
50975 if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) {
50976 drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize);
50977 drwav__write_u32ne_to_le(pWav, riffChunkSize);
50979 if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, drwav_seek_origin_start)) {
50980 drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize);
50981 drwav__write_u32ne_to_le(pWav, dataChunkSize);
50983 } else if (pWav->container == drwav_container_w64) {
50984 if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) {
50985 drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize);
50986 drwav__write_u64ne_to_le(pWav, riffChunkSize);
50988 if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, drwav_seek_origin_start)) {
50989 drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize);
50990 drwav__write_u64ne_to_le(pWav, dataChunkSize);
50992 } else if (pWav->container == drwav_container_rf64) {
50993 int ds64BodyPos = 12 + 8;
50994 if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, drwav_seek_origin_start)) {
50995 drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize);
50996 drwav__write_u64ne_to_le(pWav, riffChunkSize);
50998 if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, drwav_seek_origin_start)) {
50999 drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize);
51000 drwav__write_u64ne_to_le(pWav, dataChunkSize);
51004 if (pWav->isSequentialWrite) {
51005 if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) {
51006 result = DRWAV_INVALID_FILE;
51010 #ifndef DR_WAV_NO_STDIO
51011 if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) {
51012 fclose((FILE*)pWav->pUserData);
51017 DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut)
51020 if (pWav == NULL || bytesToRead == 0) {
51023 if (bytesToRead > pWav->bytesRemaining) {
51024 bytesToRead = (size_t)pWav->bytesRemaining;
51026 if (pBufferOut != NULL) {
51027 bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead);
51030 while (bytesRead < bytesToRead) {
51031 size_t bytesToSeek = (bytesToRead - bytesRead);
51032 if (bytesToSeek > 0x7FFFFFFF) {
51033 bytesToSeek = 0x7FFFFFFF;
51035 if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, drwav_seek_origin_current) == DRWAV_FALSE) {
51038 bytesRead += bytesToSeek;
51040 while (bytesRead < bytesToRead) {
51041 drwav_uint8 buffer[4096];
51042 size_t bytesSeeked;
51043 size_t bytesToSeek = (bytesToRead - bytesRead);
51044 if (bytesToSeek > sizeof(buffer)) {
51045 bytesToSeek = sizeof(buffer);
51047 bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek);
51048 bytesRead += bytesSeeked;
51049 if (bytesSeeked < bytesToSeek) {
51054 pWav->bytesRemaining -= bytesRead;
51057 DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
51059 drwav_uint32 bytesPerFrame;
51060 drwav_uint64 bytesToRead;
51061 if (pWav == NULL || framesToRead == 0) {
51064 if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
51067 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
51068 if (bytesPerFrame == 0) {
51071 bytesToRead = framesToRead * bytesPerFrame;
51072 if (bytesToRead > DRWAV_SIZE_MAX) {
51073 bytesToRead = (DRWAV_SIZE_MAX / bytesPerFrame) * bytesPerFrame;
51075 if (bytesToRead == 0) {
51078 return drwav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame;
51080 DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
51082 drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
51083 if (pBufferOut != NULL) {
51084 drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, drwav_get_bytes_per_pcm_frame(pWav)/pWav->channels, pWav->translatedFormatTag);
51088 DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
51090 if (drwav__is_little_endian()) {
51091 return drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
51093 return drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);
51096 DRWAV_PRIVATE drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav)
51098 if (pWav->onWrite != NULL) {
51099 return DRWAV_FALSE;
51101 if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) {
51102 return DRWAV_FALSE;
51104 if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
51105 pWav->compressed.iCurrentPCMFrame = 0;
51106 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
51107 DRWAV_ZERO_OBJECT(&pWav->msadpcm);
51108 } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
51109 DRWAV_ZERO_OBJECT(&pWav->ima);
51111 DRWAV_ASSERT(DRWAV_FALSE);
51114 pWav->bytesRemaining = pWav->dataChunkDataSize;
51117 DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex)
51119 if (pWav == NULL || pWav->onSeek == NULL) {
51120 return DRWAV_FALSE;
51122 if (pWav->onWrite != NULL) {
51123 return DRWAV_FALSE;
51125 if (pWav->totalPCMFrameCount == 0) {
51128 if (targetFrameIndex >= pWav->totalPCMFrameCount) {
51129 targetFrameIndex = pWav->totalPCMFrameCount - 1;
51131 if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
51132 if (targetFrameIndex < pWav->compressed.iCurrentPCMFrame) {
51133 if (!drwav_seek_to_first_pcm_frame(pWav)) {
51134 return DRWAV_FALSE;
51137 if (targetFrameIndex > pWav->compressed.iCurrentPCMFrame) {
51138 drwav_uint64 offsetInFrames = targetFrameIndex - pWav->compressed.iCurrentPCMFrame;
51139 drwav_int16 devnull[2048];
51140 while (offsetInFrames > 0) {
51141 drwav_uint64 framesRead = 0;
51142 drwav_uint64 framesToRead = offsetInFrames;
51143 if (framesToRead > drwav_countof(devnull)/pWav->channels) {
51144 framesToRead = drwav_countof(devnull)/pWav->channels;
51146 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
51147 framesRead = drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull);
51148 } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
51149 framesRead = drwav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull);
51151 DRWAV_ASSERT(DRWAV_FALSE);
51153 if (framesRead != framesToRead) {
51154 return DRWAV_FALSE;
51156 offsetInFrames -= framesRead;
51160 drwav_uint64 totalSizeInBytes;
51161 drwav_uint64 currentBytePos;
51162 drwav_uint64 targetBytePos;
51163 drwav_uint64 offset;
51164 totalSizeInBytes = pWav->totalPCMFrameCount * drwav_get_bytes_per_pcm_frame(pWav);
51165 DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining);
51166 currentBytePos = totalSizeInBytes - pWav->bytesRemaining;
51167 targetBytePos = targetFrameIndex * drwav_get_bytes_per_pcm_frame(pWav);
51168 if (currentBytePos < targetBytePos) {
51169 offset = (targetBytePos - currentBytePos);
51171 if (!drwav_seek_to_first_pcm_frame(pWav)) {
51172 return DRWAV_FALSE;
51174 offset = targetBytePos;
51176 while (offset > 0) {
51177 int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset);
51178 if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) {
51179 return DRWAV_FALSE;
51181 pWav->bytesRemaining -= offset32;
51182 offset -= offset32;
51187 DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData)
51189 size_t bytesWritten;
51190 if (pWav == NULL || bytesToWrite == 0 || pData == NULL) {
51193 bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);
51194 pWav->dataChunkDataSize += bytesWritten;
51195 return bytesWritten;
51197 DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
51199 drwav_uint64 bytesToWrite;
51200 drwav_uint64 bytesWritten;
51201 const drwav_uint8* pRunningData;
51202 if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
51205 bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
51206 if (bytesToWrite > DRWAV_SIZE_MAX) {
51210 pRunningData = (const drwav_uint8*)pData;
51211 while (bytesToWrite > 0) {
51212 size_t bytesJustWritten;
51213 drwav_uint64 bytesToWriteThisIteration;
51214 bytesToWriteThisIteration = bytesToWrite;
51215 DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX);
51216 bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData);
51217 if (bytesJustWritten == 0) {
51220 bytesToWrite -= bytesJustWritten;
51221 bytesWritten += bytesJustWritten;
51222 pRunningData += bytesJustWritten;
51224 return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
51226 DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
51228 drwav_uint64 bytesToWrite;
51229 drwav_uint64 bytesWritten;
51230 drwav_uint32 bytesPerSample;
51231 const drwav_uint8* pRunningData;
51232 if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
51235 bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
51236 if (bytesToWrite > DRWAV_SIZE_MAX) {
51240 pRunningData = (const drwav_uint8*)pData;
51241 bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels;
51242 while (bytesToWrite > 0) {
51243 drwav_uint8 temp[4096];
51244 drwav_uint32 sampleCount;
51245 size_t bytesJustWritten;
51246 drwav_uint64 bytesToWriteThisIteration;
51247 bytesToWriteThisIteration = bytesToWrite;
51248 DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX);
51249 sampleCount = sizeof(temp)/bytesPerSample;
51250 if (bytesToWriteThisIteration > ((drwav_uint64)sampleCount)*bytesPerSample) {
51251 bytesToWriteThisIteration = ((drwav_uint64)sampleCount)*bytesPerSample;
51253 DRWAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration);
51254 drwav__bswap_samples(temp, sampleCount, bytesPerSample, pWav->translatedFormatTag);
51255 bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp);
51256 if (bytesJustWritten == 0) {
51259 bytesToWrite -= bytesJustWritten;
51260 bytesWritten += bytesJustWritten;
51261 pRunningData += bytesJustWritten;
51263 return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
51265 DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
51267 if (drwav__is_little_endian()) {
51268 return drwav_write_pcm_frames_le(pWav, framesToWrite, pData);
51270 return drwav_write_pcm_frames_be(pWav, framesToWrite, pData);
51273 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
51275 drwav_uint64 totalFramesRead = 0;
51276 DRWAV_ASSERT(pWav != NULL);
51277 DRWAV_ASSERT(framesToRead > 0);
51278 while (pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) {
51279 DRWAV_ASSERT(framesToRead > 0);
51280 if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) {
51281 if (pWav->channels == 1) {
51282 drwav_uint8 header[7];
51283 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
51284 return totalFramesRead;
51286 pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
51287 pWav->msadpcm.predictor[0] = header[0];
51288 pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 1);
51289 pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 3);
51290 pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 5);
51291 pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0];
51292 pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1];
51293 pWav->msadpcm.cachedFrameCount = 2;
51295 drwav_uint8 header[14];
51296 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
51297 return totalFramesRead;
51299 pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
51300 pWav->msadpcm.predictor[0] = header[0];
51301 pWav->msadpcm.predictor[1] = header[1];
51302 pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 2);
51303 pWav->msadpcm.delta[1] = drwav_bytes_to_s16(header + 4);
51304 pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 6);
51305 pWav->msadpcm.prevFrames[1][1] = (drwav_int32)drwav_bytes_to_s16(header + 8);
51306 pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 10);
51307 pWav->msadpcm.prevFrames[1][0] = (drwav_int32)drwav_bytes_to_s16(header + 12);
51308 pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0];
51309 pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0];
51310 pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1];
51311 pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1];
51312 pWav->msadpcm.cachedFrameCount = 2;
51315 while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) {
51316 if (pBufferOut != NULL) {
51317 drwav_uint32 iSample = 0;
51318 for (iSample = 0; iSample < pWav->channels; iSample += 1) {
51319 pBufferOut[iSample] = (drwav_int16)pWav->msadpcm.cachedFrames[(drwav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample];
51321 pBufferOut += pWav->channels;
51324 totalFramesRead += 1;
51325 pWav->compressed.iCurrentPCMFrame += 1;
51326 pWav->msadpcm.cachedFrameCount -= 1;
51328 if (framesToRead == 0) {
51331 if (pWav->msadpcm.cachedFrameCount == 0) {
51332 if (pWav->msadpcm.bytesRemainingInBlock == 0) {
51335 static drwav_int32 adaptationTable[] = {
51336 230, 230, 230, 230, 307, 409, 512, 614,
51337 768, 614, 512, 409, 307, 230, 230, 230
51339 static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
51340 static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
51341 drwav_uint8 nibbles;
51342 drwav_int32 nibble0;
51343 drwav_int32 nibble1;
51344 if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) {
51345 return totalFramesRead;
51347 pWav->msadpcm.bytesRemainingInBlock -= 1;
51348 nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; }
51349 nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; }
51350 if (pWav->channels == 1) {
51351 drwav_int32 newSample0;
51352 drwav_int32 newSample1;
51353 newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
51354 newSample0 += nibble0 * pWav->msadpcm.delta[0];
51355 newSample0 = drwav_clamp(newSample0, -32768, 32767);
51356 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
51357 if (pWav->msadpcm.delta[0] < 16) {
51358 pWav->msadpcm.delta[0] = 16;
51360 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
51361 pWav->msadpcm.prevFrames[0][1] = newSample0;
51362 newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
51363 newSample1 += nibble1 * pWav->msadpcm.delta[0];
51364 newSample1 = drwav_clamp(newSample1, -32768, 32767);
51365 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;
51366 if (pWav->msadpcm.delta[0] < 16) {
51367 pWav->msadpcm.delta[0] = 16;
51369 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
51370 pWav->msadpcm.prevFrames[0][1] = newSample1;
51371 pWav->msadpcm.cachedFrames[2] = newSample0;
51372 pWav->msadpcm.cachedFrames[3] = newSample1;
51373 pWav->msadpcm.cachedFrameCount = 2;
51375 drwav_int32 newSample0;
51376 drwav_int32 newSample1;
51377 newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
51378 newSample0 += nibble0 * pWav->msadpcm.delta[0];
51379 newSample0 = drwav_clamp(newSample0, -32768, 32767);
51380 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
51381 if (pWav->msadpcm.delta[0] < 16) {
51382 pWav->msadpcm.delta[0] = 16;
51384 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
51385 pWav->msadpcm.prevFrames[0][1] = newSample0;
51386 newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
51387 newSample1 += nibble1 * pWav->msadpcm.delta[1];
51388 newSample1 = drwav_clamp(newSample1, -32768, 32767);
51389 pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;
51390 if (pWav->msadpcm.delta[1] < 16) {
51391 pWav->msadpcm.delta[1] = 16;
51393 pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1];
51394 pWav->msadpcm.prevFrames[1][1] = newSample1;
51395 pWav->msadpcm.cachedFrames[2] = newSample0;
51396 pWav->msadpcm.cachedFrames[3] = newSample1;
51397 pWav->msadpcm.cachedFrameCount = 1;
51402 return totalFramesRead;
51404 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
51406 drwav_uint64 totalFramesRead = 0;
51407 drwav_uint32 iChannel;
51408 static drwav_int32 indexTable[16] = {
51409 -1, -1, -1, -1, 2, 4, 6, 8,
51410 -1, -1, -1, -1, 2, 4, 6, 8
51412 static drwav_int32 stepTable[89] = {
51413 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
51414 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
51415 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
51416 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
51417 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
51418 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
51419 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
51420 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
51421 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
51423 DRWAV_ASSERT(pWav != NULL);
51424 DRWAV_ASSERT(framesToRead > 0);
51425 while (pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) {
51426 DRWAV_ASSERT(framesToRead > 0);
51427 if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) {
51428 if (pWav->channels == 1) {
51429 drwav_uint8 header[4];
51430 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
51431 return totalFramesRead;
51433 pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
51434 if (header[2] >= drwav_countof(stepTable)) {
51435 pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current);
51436 pWav->ima.bytesRemainingInBlock = 0;
51437 return totalFramesRead;
51439 pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0);
51440 pWav->ima.stepIndex[0] = header[2];
51441 pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0];
51442 pWav->ima.cachedFrameCount = 1;
51444 drwav_uint8 header[8];
51445 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
51446 return totalFramesRead;
51448 pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
51449 if (header[2] >= drwav_countof(stepTable) || header[6] >= drwav_countof(stepTable)) {
51450 pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current);
51451 pWav->ima.bytesRemainingInBlock = 0;
51452 return totalFramesRead;
51454 pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0);
51455 pWav->ima.stepIndex[0] = header[2];
51456 pWav->ima.predictor[1] = drwav_bytes_to_s16(header + 4);
51457 pWav->ima.stepIndex[1] = header[6];
51458 pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0];
51459 pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1];
51460 pWav->ima.cachedFrameCount = 1;
51463 while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) {
51464 if (pBufferOut != NULL) {
51465 drwav_uint32 iSample;
51466 for (iSample = 0; iSample < pWav->channels; iSample += 1) {
51467 pBufferOut[iSample] = (drwav_int16)pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample];
51469 pBufferOut += pWav->channels;
51472 totalFramesRead += 1;
51473 pWav->compressed.iCurrentPCMFrame += 1;
51474 pWav->ima.cachedFrameCount -= 1;
51476 if (framesToRead == 0) {
51479 if (pWav->ima.cachedFrameCount == 0) {
51480 if (pWav->ima.bytesRemainingInBlock == 0) {
51483 pWav->ima.cachedFrameCount = 8;
51484 for (iChannel = 0; iChannel < pWav->channels; ++iChannel) {
51485 drwav_uint32 iByte;
51486 drwav_uint8 nibbles[4];
51487 if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) {
51488 pWav->ima.cachedFrameCount = 0;
51489 return totalFramesRead;
51491 pWav->ima.bytesRemainingInBlock -= 4;
51492 for (iByte = 0; iByte < 4; ++iByte) {
51493 drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0);
51494 drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4);
51495 drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]];
51496 drwav_int32 predictor = pWav->ima.predictor[iChannel];
51497 drwav_int32 diff = step >> 3;
51498 if (nibble0 & 1) diff += step >> 2;
51499 if (nibble0 & 2) diff += step >> 1;
51500 if (nibble0 & 4) diff += step;
51501 if (nibble0 & 8) diff = -diff;
51502 predictor = drwav_clamp(predictor + diff, -32768, 32767);
51503 pWav->ima.predictor[iChannel] = predictor;
51504 pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1);
51505 pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor;
51506 step = stepTable[pWav->ima.stepIndex[iChannel]];
51507 predictor = pWav->ima.predictor[iChannel];
51509 if (nibble1 & 1) diff += step >> 2;
51510 if (nibble1 & 2) diff += step >> 1;
51511 if (nibble1 & 4) diff += step;
51512 if (nibble1 & 8) diff = -diff;
51513 predictor = drwav_clamp(predictor + diff, -32768, 32767);
51514 pWav->ima.predictor[iChannel] = predictor;
51515 pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1);
51516 pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor;
51522 return totalFramesRead;
51524 #ifndef DR_WAV_NO_CONVERSION_API
51525 static unsigned short g_drwavAlawTable[256] = {
51526 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
51527 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
51528 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
51529 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00,
51530 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58,
51531 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58,
51532 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960,
51533 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0,
51534 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80,
51535 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40,
51536 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00,
51537 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500,
51538 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8,
51539 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8,
51540 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0,
51541 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350
51543 static unsigned short g_drwavMulawTable[256] = {
51544 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
51545 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
51546 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
51547 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844,
51548 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64,
51549 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74,
51550 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C,
51551 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000,
51552 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C,
51553 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C,
51554 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC,
51555 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC,
51556 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C,
51557 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C,
51558 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084,
51559 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
51561 static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn)
51563 return (short)g_drwavAlawTable[sampleIn];
51565 static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn)
51567 return (short)g_drwavMulawTable[sampleIn];
51569 DRWAV_PRIVATE void drwav__pcm_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
51572 if (bytesPerSample == 1) {
51573 drwav_u8_to_s16(pOut, pIn, totalSampleCount);
51576 if (bytesPerSample == 2) {
51577 for (i = 0; i < totalSampleCount; ++i) {
51578 *pOut++ = ((const drwav_int16*)pIn)[i];
51582 if (bytesPerSample == 3) {
51583 drwav_s24_to_s16(pOut, pIn, totalSampleCount);
51586 if (bytesPerSample == 4) {
51587 drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount);
51590 if (bytesPerSample > 8) {
51591 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
51594 for (i = 0; i < totalSampleCount; ++i) {
51595 drwav_uint64 sample = 0;
51596 unsigned int shift = (8 - bytesPerSample) * 8;
51598 for (j = 0; j < bytesPerSample; j += 1) {
51599 DRWAV_ASSERT(j < 8);
51600 sample |= (drwav_uint64)(pIn[j]) << shift;
51604 *pOut++ = (drwav_int16)((drwav_int64)sample >> 48);
51607 DRWAV_PRIVATE void drwav__ieee_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
51609 if (bytesPerSample == 4) {
51610 drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount);
51612 } else if (bytesPerSample == 8) {
51613 drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount);
51616 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
51620 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
51622 drwav_uint64 totalFramesRead;
51623 drwav_uint8 sampleData[4096];
51624 drwav_uint32 bytesPerFrame;
51625 if ((pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) {
51626 return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
51628 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
51629 if (bytesPerFrame == 0) {
51632 totalFramesRead = 0;
51633 while (framesToRead > 0) {
51634 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
51635 if (framesRead == 0) {
51638 drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);
51639 pBufferOut += framesRead*pWav->channels;
51640 framesToRead -= framesRead;
51641 totalFramesRead += framesRead;
51643 return totalFramesRead;
51645 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
51647 drwav_uint64 totalFramesRead;
51648 drwav_uint8 sampleData[4096];
51649 drwav_uint32 bytesPerFrame;
51650 if (pBufferOut == NULL) {
51651 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
51653 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
51654 if (bytesPerFrame == 0) {
51657 totalFramesRead = 0;
51658 while (framesToRead > 0) {
51659 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
51660 if (framesRead == 0) {
51663 drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);
51664 pBufferOut += framesRead*pWav->channels;
51665 framesToRead -= framesRead;
51666 totalFramesRead += framesRead;
51668 return totalFramesRead;
51670 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
51672 drwav_uint64 totalFramesRead;
51673 drwav_uint8 sampleData[4096];
51674 drwav_uint32 bytesPerFrame;
51675 if (pBufferOut == NULL) {
51676 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
51678 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
51679 if (bytesPerFrame == 0) {
51682 totalFramesRead = 0;
51683 while (framesToRead > 0) {
51684 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
51685 if (framesRead == 0) {
51688 drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
51689 pBufferOut += framesRead*pWav->channels;
51690 framesToRead -= framesRead;
51691 totalFramesRead += framesRead;
51693 return totalFramesRead;
51695 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
51697 drwav_uint64 totalFramesRead;
51698 drwav_uint8 sampleData[4096];
51699 drwav_uint32 bytesPerFrame;
51700 if (pBufferOut == NULL) {
51701 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
51703 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
51704 if (bytesPerFrame == 0) {
51707 totalFramesRead = 0;
51708 while (framesToRead > 0) {
51709 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
51710 if (framesRead == 0) {
51713 drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
51714 pBufferOut += framesRead*pWav->channels;
51715 framesToRead -= framesRead;
51716 totalFramesRead += framesRead;
51718 return totalFramesRead;
51720 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
51722 if (pWav == NULL || framesToRead == 0) {
51725 if (pBufferOut == NULL) {
51726 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
51728 if (framesToRead * pWav->channels * sizeof(drwav_int16) > DRWAV_SIZE_MAX) {
51729 framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16) / pWav->channels;
51731 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
51732 return drwav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut);
51734 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
51735 return drwav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut);
51737 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
51738 return drwav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut);
51740 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
51741 return drwav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut);
51743 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
51744 return drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut);
51746 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
51747 return drwav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut);
51751 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
51753 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
51754 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
51755 drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
51759 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
51761 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
51762 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
51763 drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
51767 DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
51771 for (i = 0; i < sampleCount; ++i) {
51775 pOut[i] = (short)r;
51778 DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
51782 for (i = 0; i < sampleCount; ++i) {
51783 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;
51785 pOut[i] = (short)r;
51788 DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount)
51792 for (i = 0; i < sampleCount; ++i) {
51795 pOut[i] = (short)r;
51798 DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount)
51802 for (i = 0; i < sampleCount; ++i) {
51805 c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
51807 r = (int)(c * 32767.5f);
51809 pOut[i] = (short)r;
51812 DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount)
51816 for (i = 0; i < sampleCount; ++i) {
51819 c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
51821 r = (int)(c * 32767.5);
51823 pOut[i] = (short)r;
51826 DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
51829 for (i = 0; i < sampleCount; ++i) {
51830 pOut[i] = drwav__alaw_to_s16(pIn[i]);
51833 DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
51836 for (i = 0; i < sampleCount; ++i) {
51837 pOut[i] = drwav__mulaw_to_s16(pIn[i]);
51840 DRWAV_PRIVATE void drwav__pcm_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
51843 if (bytesPerSample == 1) {
51844 drwav_u8_to_f32(pOut, pIn, sampleCount);
51847 if (bytesPerSample == 2) {
51848 drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount);
51851 if (bytesPerSample == 3) {
51852 drwav_s24_to_f32(pOut, pIn, sampleCount);
51855 if (bytesPerSample == 4) {
51856 drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount);
51859 if (bytesPerSample > 8) {
51860 DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
51863 for (i = 0; i < sampleCount; ++i) {
51864 drwav_uint64 sample = 0;
51865 unsigned int shift = (8 - bytesPerSample) * 8;
51867 for (j = 0; j < bytesPerSample; j += 1) {
51868 DRWAV_ASSERT(j < 8);
51869 sample |= (drwav_uint64)(pIn[j]) << shift;
51873 *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0);
51876 DRWAV_PRIVATE void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
51878 if (bytesPerSample == 4) {
51880 for (i = 0; i < sampleCount; ++i) {
51881 *pOut++ = ((const float*)pIn)[i];
51884 } else if (bytesPerSample == 8) {
51885 drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount);
51888 DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
51892 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
51894 drwav_uint64 totalFramesRead;
51895 drwav_uint8 sampleData[4096];
51896 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
51897 if (bytesPerFrame == 0) {
51900 totalFramesRead = 0;
51901 while (framesToRead > 0) {
51902 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
51903 if (framesRead == 0) {
51906 drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)framesRead*pWav->channels, bytesPerFrame/pWav->channels);
51907 pBufferOut += framesRead*pWav->channels;
51908 framesToRead -= framesRead;
51909 totalFramesRead += framesRead;
51911 return totalFramesRead;
51913 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__msadpcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
51915 drwav_uint64 totalFramesRead = 0;
51916 drwav_int16 samples16[2048];
51917 while (framesToRead > 0) {
51918 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16);
51919 if (framesRead == 0) {
51922 drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
51923 pBufferOut += framesRead*pWav->channels;
51924 framesToRead -= framesRead;
51925 totalFramesRead += framesRead;
51927 return totalFramesRead;
51929 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
51931 drwav_uint64 totalFramesRead = 0;
51932 drwav_int16 samples16[2048];
51933 while (framesToRead > 0) {
51934 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16);
51935 if (framesRead == 0) {
51938 drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
51939 pBufferOut += framesRead*pWav->channels;
51940 framesToRead -= framesRead;
51941 totalFramesRead += framesRead;
51943 return totalFramesRead;
51945 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
51947 drwav_uint64 totalFramesRead;
51948 drwav_uint8 sampleData[4096];
51949 drwav_uint32 bytesPerFrame;
51950 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) {
51951 return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
51953 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
51954 if (bytesPerFrame == 0) {
51957 totalFramesRead = 0;
51958 while (framesToRead > 0) {
51959 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
51960 if (framesRead == 0) {
51963 drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);
51964 pBufferOut += framesRead*pWav->channels;
51965 framesToRead -= framesRead;
51966 totalFramesRead += framesRead;
51968 return totalFramesRead;
51970 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
51972 drwav_uint64 totalFramesRead;
51973 drwav_uint8 sampleData[4096];
51974 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
51975 if (bytesPerFrame == 0) {
51978 totalFramesRead = 0;
51979 while (framesToRead > 0) {
51980 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
51981 if (framesRead == 0) {
51984 drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
51985 pBufferOut += framesRead*pWav->channels;
51986 framesToRead -= framesRead;
51987 totalFramesRead += framesRead;
51989 return totalFramesRead;
51991 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
51993 drwav_uint64 totalFramesRead;
51994 drwav_uint8 sampleData[4096];
51995 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
51996 if (bytesPerFrame == 0) {
51999 totalFramesRead = 0;
52000 while (framesToRead > 0) {
52001 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
52002 if (framesRead == 0) {
52005 drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
52006 pBufferOut += framesRead*pWav->channels;
52007 framesToRead -= framesRead;
52008 totalFramesRead += framesRead;
52010 return totalFramesRead;
52012 DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
52014 if (pWav == NULL || framesToRead == 0) {
52017 if (pBufferOut == NULL) {
52018 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
52020 if (framesToRead * pWav->channels * sizeof(float) > DRWAV_SIZE_MAX) {
52021 framesToRead = DRWAV_SIZE_MAX / sizeof(float) / pWav->channels;
52023 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
52024 return drwav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut);
52026 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
52027 return drwav_read_pcm_frames_f32__msadpcm(pWav, framesToRead, pBufferOut);
52029 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
52030 return drwav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut);
52032 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
52033 return drwav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut);
52035 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
52036 return drwav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut);
52038 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
52039 return drwav_read_pcm_frames_f32__ima(pWav, framesToRead, pBufferOut);
52043 DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
52045 drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
52046 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
52047 drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
52051 DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
52053 drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
52054 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
52055 drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
52059 DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
52062 if (pOut == NULL || pIn == NULL) {
52065 #ifdef DR_WAV_LIBSNDFILE_COMPAT
52066 for (i = 0; i < sampleCount; ++i) {
52067 *pOut++ = (pIn[i] / 256.0f) * 2 - 1;
52070 for (i = 0; i < sampleCount; ++i) {
52072 x = x * 0.00784313725490196078f;
52078 DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount)
52081 if (pOut == NULL || pIn == NULL) {
52084 for (i = 0; i < sampleCount; ++i) {
52085 *pOut++ = pIn[i] * 0.000030517578125f;
52088 DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
52091 if (pOut == NULL || pIn == NULL) {
52094 for (i = 0; i < sampleCount; ++i) {
52096 drwav_uint32 a = ((drwav_uint32)(pIn[i*3+0]) << 8);
52097 drwav_uint32 b = ((drwav_uint32)(pIn[i*3+1]) << 16);
52098 drwav_uint32 c = ((drwav_uint32)(pIn[i*3+2]) << 24);
52099 x = (double)((drwav_int32)(a | b | c) >> 8);
52100 *pOut++ = (float)(x * 0.00000011920928955078125);
52103 DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount)
52106 if (pOut == NULL || pIn == NULL) {
52109 for (i = 0; i < sampleCount; ++i) {
52110 *pOut++ = (float)(pIn[i] / 2147483648.0);
52113 DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount)
52116 if (pOut == NULL || pIn == NULL) {
52119 for (i = 0; i < sampleCount; ++i) {
52120 *pOut++ = (float)pIn[i];
52123 DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
52126 if (pOut == NULL || pIn == NULL) {
52129 for (i = 0; i < sampleCount; ++i) {
52130 *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f;
52133 DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
52136 if (pOut == NULL || pIn == NULL) {
52139 for (i = 0; i < sampleCount; ++i) {
52140 *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f;
52143 DRWAV_PRIVATE void drwav__pcm_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
52146 if (bytesPerSample == 1) {
52147 drwav_u8_to_s32(pOut, pIn, totalSampleCount);
52150 if (bytesPerSample == 2) {
52151 drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount);
52154 if (bytesPerSample == 3) {
52155 drwav_s24_to_s32(pOut, pIn, totalSampleCount);
52158 if (bytesPerSample == 4) {
52159 for (i = 0; i < totalSampleCount; ++i) {
52160 *pOut++ = ((const drwav_int32*)pIn)[i];
52164 if (bytesPerSample > 8) {
52165 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
52168 for (i = 0; i < totalSampleCount; ++i) {
52169 drwav_uint64 sample = 0;
52170 unsigned int shift = (8 - bytesPerSample) * 8;
52172 for (j = 0; j < bytesPerSample; j += 1) {
52173 DRWAV_ASSERT(j < 8);
52174 sample |= (drwav_uint64)(pIn[j]) << shift;
52178 *pOut++ = (drwav_int32)((drwav_int64)sample >> 32);
52181 DRWAV_PRIVATE void drwav__ieee_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
52183 if (bytesPerSample == 4) {
52184 drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount);
52186 } else if (bytesPerSample == 8) {
52187 drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount);
52190 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
52194 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
52196 drwav_uint64 totalFramesRead;
52197 drwav_uint8 sampleData[4096];
52198 drwav_uint32 bytesPerFrame;
52199 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) {
52200 return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
52202 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
52203 if (bytesPerFrame == 0) {
52206 totalFramesRead = 0;
52207 while (framesToRead > 0) {
52208 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
52209 if (framesRead == 0) {
52212 drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);
52213 pBufferOut += framesRead*pWav->channels;
52214 framesToRead -= framesRead;
52215 totalFramesRead += framesRead;
52217 return totalFramesRead;
52219 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
52221 drwav_uint64 totalFramesRead = 0;
52222 drwav_int16 samples16[2048];
52223 while (framesToRead > 0) {
52224 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16);
52225 if (framesRead == 0) {
52228 drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
52229 pBufferOut += framesRead*pWav->channels;
52230 framesToRead -= framesRead;
52231 totalFramesRead += framesRead;
52233 return totalFramesRead;
52235 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
52237 drwav_uint64 totalFramesRead = 0;
52238 drwav_int16 samples16[2048];
52239 while (framesToRead > 0) {
52240 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16);
52241 if (framesRead == 0) {
52244 drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
52245 pBufferOut += framesRead*pWav->channels;
52246 framesToRead -= framesRead;
52247 totalFramesRead += framesRead;
52249 return totalFramesRead;
52251 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
52253 drwav_uint64 totalFramesRead;
52254 drwav_uint8 sampleData[4096];
52255 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
52256 if (bytesPerFrame == 0) {
52259 totalFramesRead = 0;
52260 while (framesToRead > 0) {
52261 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
52262 if (framesRead == 0) {
52265 drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);
52266 pBufferOut += framesRead*pWav->channels;
52267 framesToRead -= framesRead;
52268 totalFramesRead += framesRead;
52270 return totalFramesRead;
52272 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
52274 drwav_uint64 totalFramesRead;
52275 drwav_uint8 sampleData[4096];
52276 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
52277 if (bytesPerFrame == 0) {
52280 totalFramesRead = 0;
52281 while (framesToRead > 0) {
52282 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
52283 if (framesRead == 0) {
52286 drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
52287 pBufferOut += framesRead*pWav->channels;
52288 framesToRead -= framesRead;
52289 totalFramesRead += framesRead;
52291 return totalFramesRead;
52293 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
52295 drwav_uint64 totalFramesRead;
52296 drwav_uint8 sampleData[4096];
52297 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
52298 if (bytesPerFrame == 0) {
52301 totalFramesRead = 0;
52302 while (framesToRead > 0) {
52303 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
52304 if (framesRead == 0) {
52307 drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
52308 pBufferOut += framesRead*pWav->channels;
52309 framesToRead -= framesRead;
52310 totalFramesRead += framesRead;
52312 return totalFramesRead;
52314 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
52316 if (pWav == NULL || framesToRead == 0) {
52319 if (pBufferOut == NULL) {
52320 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
52322 if (framesToRead * pWav->channels * sizeof(drwav_int32) > DRWAV_SIZE_MAX) {
52323 framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32) / pWav->channels;
52325 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
52326 return drwav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut);
52328 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
52329 return drwav_read_pcm_frames_s32__msadpcm(pWav, framesToRead, pBufferOut);
52331 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
52332 return drwav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut);
52334 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
52335 return drwav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut);
52337 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
52338 return drwav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut);
52340 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
52341 return drwav_read_pcm_frames_s32__ima(pWav, framesToRead, pBufferOut);
52345 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
52347 drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
52348 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
52349 drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
52353 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
52355 drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
52356 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
52357 drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
52361 DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
52364 if (pOut == NULL || pIn == NULL) {
52367 for (i = 0; i < sampleCount; ++i) {
52368 *pOut++ = ((int)pIn[i] - 128) << 24;
52371 DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount)
52374 if (pOut == NULL || pIn == NULL) {
52377 for (i = 0; i < sampleCount; ++i) {
52378 *pOut++ = pIn[i] << 16;
52381 DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
52384 if (pOut == NULL || pIn == NULL) {
52387 for (i = 0; i < sampleCount; ++i) {
52388 unsigned int s0 = pIn[i*3 + 0];
52389 unsigned int s1 = pIn[i*3 + 1];
52390 unsigned int s2 = pIn[i*3 + 2];
52391 drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24));
52392 *pOut++ = sample32;
52395 DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount)
52398 if (pOut == NULL || pIn == NULL) {
52401 for (i = 0; i < sampleCount; ++i) {
52402 *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
52405 DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount)
52408 if (pOut == NULL || pIn == NULL) {
52411 for (i = 0; i < sampleCount; ++i) {
52412 *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
52415 DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
52418 if (pOut == NULL || pIn == NULL) {
52421 for (i = 0; i < sampleCount; ++i) {
52422 *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16;
52425 DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
52428 if (pOut == NULL || pIn == NULL) {
52431 for (i= 0; i < sampleCount; ++i) {
52432 *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16;
52435 DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
52437 drwav_uint64 sampleDataSize;
52438 drwav_int16* pSampleData;
52439 drwav_uint64 framesRead;
52440 DRWAV_ASSERT(pWav != NULL);
52441 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16);
52442 if (sampleDataSize > DRWAV_SIZE_MAX) {
52443 drwav_uninit(pWav);
52446 pSampleData = (drwav_int16*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
52447 if (pSampleData == NULL) {
52448 drwav_uninit(pWav);
52451 framesRead = drwav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
52452 if (framesRead != pWav->totalPCMFrameCount) {
52453 drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
52454 drwav_uninit(pWav);
52457 drwav_uninit(pWav);
52459 *sampleRate = pWav->sampleRate;
52462 *channels = pWav->channels;
52464 if (totalFrameCount) {
52465 *totalFrameCount = pWav->totalPCMFrameCount;
52467 return pSampleData;
52469 DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
52471 drwav_uint64 sampleDataSize;
52472 float* pSampleData;
52473 drwav_uint64 framesRead;
52474 DRWAV_ASSERT(pWav != NULL);
52475 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float);
52476 if (sampleDataSize > DRWAV_SIZE_MAX) {
52477 drwav_uninit(pWav);
52480 pSampleData = (float*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
52481 if (pSampleData == NULL) {
52482 drwav_uninit(pWav);
52485 framesRead = drwav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
52486 if (framesRead != pWav->totalPCMFrameCount) {
52487 drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
52488 drwav_uninit(pWav);
52491 drwav_uninit(pWav);
52493 *sampleRate = pWav->sampleRate;
52496 *channels = pWav->channels;
52498 if (totalFrameCount) {
52499 *totalFrameCount = pWav->totalPCMFrameCount;
52501 return pSampleData;
52503 DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
52505 drwav_uint64 sampleDataSize;
52506 drwav_int32* pSampleData;
52507 drwav_uint64 framesRead;
52508 DRWAV_ASSERT(pWav != NULL);
52509 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32);
52510 if (sampleDataSize > DRWAV_SIZE_MAX) {
52511 drwav_uninit(pWav);
52514 pSampleData = (drwav_int32*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
52515 if (pSampleData == NULL) {
52516 drwav_uninit(pWav);
52519 framesRead = drwav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
52520 if (framesRead != pWav->totalPCMFrameCount) {
52521 drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
52522 drwav_uninit(pWav);
52525 drwav_uninit(pWav);
52527 *sampleRate = pWav->sampleRate;
52530 *channels = pWav->channels;
52532 if (totalFrameCount) {
52533 *totalFrameCount = pWav->totalPCMFrameCount;
52535 return pSampleData;
52537 DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
52543 if (sampleRateOut) {
52544 *sampleRateOut = 0;
52546 if (totalFrameCountOut) {
52547 *totalFrameCountOut = 0;
52549 if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
52552 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
52554 DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
52560 if (sampleRateOut) {
52561 *sampleRateOut = 0;
52563 if (totalFrameCountOut) {
52564 *totalFrameCountOut = 0;
52566 if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
52569 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
52571 DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
52577 if (sampleRateOut) {
52578 *sampleRateOut = 0;
52580 if (totalFrameCountOut) {
52581 *totalFrameCountOut = 0;
52583 if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
52586 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
52588 #ifndef DR_WAV_NO_STDIO
52589 DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
52595 if (sampleRateOut) {
52596 *sampleRateOut = 0;
52598 if (totalFrameCountOut) {
52599 *totalFrameCountOut = 0;
52601 if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
52604 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
52606 DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
52612 if (sampleRateOut) {
52613 *sampleRateOut = 0;
52615 if (totalFrameCountOut) {
52616 *totalFrameCountOut = 0;
52618 if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
52621 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
52623 DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
52629 if (sampleRateOut) {
52630 *sampleRateOut = 0;
52632 if (totalFrameCountOut) {
52633 *totalFrameCountOut = 0;
52635 if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
52638 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
52640 DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
52643 if (sampleRateOut) {
52644 *sampleRateOut = 0;
52649 if (totalFrameCountOut) {
52650 *totalFrameCountOut = 0;
52652 if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
52655 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
52657 DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
52660 if (sampleRateOut) {
52661 *sampleRateOut = 0;
52666 if (totalFrameCountOut) {
52667 *totalFrameCountOut = 0;
52669 if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
52672 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
52674 DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
52677 if (sampleRateOut) {
52678 *sampleRateOut = 0;
52683 if (totalFrameCountOut) {
52684 *totalFrameCountOut = 0;
52686 if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
52689 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
52692 DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
52698 if (sampleRateOut) {
52699 *sampleRateOut = 0;
52701 if (totalFrameCountOut) {
52702 *totalFrameCountOut = 0;
52704 if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
52707 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
52709 DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
52715 if (sampleRateOut) {
52716 *sampleRateOut = 0;
52718 if (totalFrameCountOut) {
52719 *totalFrameCountOut = 0;
52721 if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
52724 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
52726 DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
52732 if (sampleRateOut) {
52733 *sampleRateOut = 0;
52735 if (totalFrameCountOut) {
52736 *totalFrameCountOut = 0;
52738 if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
52741 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
52744 DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks)
52746 if (pAllocationCallbacks != NULL) {
52747 drwav__free_from_callbacks(p, pAllocationCallbacks);
52749 drwav__free_default(p, NULL);
52752 DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data)
52754 return (data[0] << 0) | (data[1] << 8);
52756 DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data)
52758 return (short)drwav_bytes_to_u16(data);
52760 DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data)
52762 return (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
52764 DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data)
52766 return (drwav_int32)drwav_bytes_to_u32(data);
52768 DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data)
52771 ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) |
52772 ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56);
52774 DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data)
52776 return (drwav_int64)drwav_bytes_to_u64(data);
52778 DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16])
52781 for (i = 0; i < 16; i += 1) {
52782 if (a[i] != b[i]) {
52783 return DRWAV_FALSE;
52788 DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b)
52798 #endif /* DRWAV_IMPLEMENTATION */
52799 #endif /* MA_NO_WAV */
52801 #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)
52802 #if !defined(DR_FLAC_IMPLEMENTATION) && !defined(DRFLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
52803 /* dr_flac_c begin */
52806 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
52807 #pragma GCC diagnostic push
52809 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
52813 #ifndef _BSD_SOURCE
52814 #define _BSD_SOURCE
52816 #ifndef _DEFAULT_SOURCE
52817 #define _DEFAULT_SOURCE
52822 #include <endian.h>
52824 #include <stdlib.h>
52825 #include <string.h>
52827 #define DRFLAC_INLINE __forceinline
52828 #elif defined(__GNUC__)
52829 #if defined(__STRICT_ANSI__)
52830 #define DRFLAC_INLINE __inline__ __attribute__((always_inline))
52832 #define DRFLAC_INLINE inline __attribute__((always_inline))
52834 #elif defined(__WATCOMC__)
52835 #define DRFLAC_INLINE __inline
52837 #define DRFLAC_INLINE
52839 #if defined(__x86_64__) || defined(_M_X64)
52841 #elif defined(__i386) || defined(_M_IX86)
52843 #elif defined(__arm__) || defined(_M_ARM) || defined(_M_ARM64)
52846 #if !defined(DR_FLAC_NO_SIMD)
52847 #if defined(DRFLAC_X64) || defined(DRFLAC_X86)
52848 #if defined(_MSC_VER) && !defined(__clang__)
52849 #if _MSC_VER >= 1400 && !defined(DRFLAC_NO_SSE2)
52850 #define DRFLAC_SUPPORT_SSE2
52852 #if _MSC_VER >= 1600 && !defined(DRFLAC_NO_SSE41)
52853 #define DRFLAC_SUPPORT_SSE41
52855 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
52856 #if defined(__SSE2__) && !defined(DRFLAC_NO_SSE2)
52857 #define DRFLAC_SUPPORT_SSE2
52859 #if defined(__SSE4_1__) && !defined(DRFLAC_NO_SSE41)
52860 #define DRFLAC_SUPPORT_SSE41
52863 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
52864 #if !defined(DRFLAC_SUPPORT_SSE2) && !defined(DRFLAC_NO_SSE2) && __has_include(<emmintrin.h>)
52865 #define DRFLAC_SUPPORT_SSE2
52867 #if !defined(DRFLAC_SUPPORT_SSE41) && !defined(DRFLAC_NO_SSE41) && __has_include(<smmintrin.h>)
52868 #define DRFLAC_SUPPORT_SSE41
52871 #if defined(DRFLAC_SUPPORT_SSE41)
52872 #include <smmintrin.h>
52873 #elif defined(DRFLAC_SUPPORT_SSE2)
52874 #include <emmintrin.h>
52877 #if defined(DRFLAC_ARM)
52878 #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
52879 #define DRFLAC_SUPPORT_NEON
52881 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
52882 #if !defined(DRFLAC_SUPPORT_NEON) && !defined(DRFLAC_NO_NEON) && __has_include(<arm_neon.h>)
52883 #define DRFLAC_SUPPORT_NEON
52886 #if defined(DRFLAC_SUPPORT_NEON)
52887 #include <arm_neon.h>
52891 #if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64))
52892 #if defined(_MSC_VER) && !defined(__clang__)
52893 #if _MSC_VER >= 1400
52894 #include <intrin.h>
52895 static void drflac__cpuid(int info[4], int fid)
52897 __cpuid(info, fid);
52900 #define DRFLAC_NO_CPUID
52903 #if defined(__GNUC__) || defined(__clang__)
52904 static void drflac__cpuid(int info[4], int fid)
52906 #if defined(DRFLAC_X86) && defined(__PIC__)
52907 __asm__ __volatile__ (
52908 "xchg{l} {%%}ebx, %k1;"
52910 "xchg{l} {%%}ebx, %k1;"
52911 : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
52914 __asm__ __volatile__ (
52915 "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
52920 #define DRFLAC_NO_CPUID
52924 #define DRFLAC_NO_CPUID
52926 static DRFLAC_INLINE drflac_bool32 drflac_has_sse2(void)
52928 #if defined(DRFLAC_SUPPORT_SSE2)
52929 #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE2)
52930 #if defined(DRFLAC_X64)
52931 return DRFLAC_TRUE;
52932 #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
52933 return DRFLAC_TRUE;
52935 #if defined(DRFLAC_NO_CPUID)
52936 return DRFLAC_FALSE;
52939 drflac__cpuid(info, 1);
52940 return (info[3] & (1 << 26)) != 0;
52944 return DRFLAC_FALSE;
52947 return DRFLAC_FALSE;
52950 static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void)
52952 #if defined(DRFLAC_SUPPORT_SSE41)
52953 #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41)
52954 #if defined(DRFLAC_X64)
52955 return DRFLAC_TRUE;
52956 #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE4_1__)
52957 return DRFLAC_TRUE;
52959 #if defined(DRFLAC_NO_CPUID)
52960 return DRFLAC_FALSE;
52963 drflac__cpuid(info, 1);
52964 return (info[2] & (1 << 19)) != 0;
52968 return DRFLAC_FALSE;
52971 return DRFLAC_FALSE;
52974 #if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) && !defined(__clang__)
52975 #define DRFLAC_HAS_LZCNT_INTRINSIC
52976 #elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
52977 #define DRFLAC_HAS_LZCNT_INTRINSIC
52978 #elif defined(__clang__)
52979 #if defined(__has_builtin)
52980 #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl)
52981 #define DRFLAC_HAS_LZCNT_INTRINSIC
52985 #if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__)
52986 #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
52987 #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
52988 #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
52989 #elif defined(__clang__)
52990 #if defined(__has_builtin)
52991 #if __has_builtin(__builtin_bswap16)
52992 #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
52994 #if __has_builtin(__builtin_bswap32)
52995 #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
52997 #if __has_builtin(__builtin_bswap64)
52998 #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
53001 #elif defined(__GNUC__)
53002 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
53003 #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
53004 #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
53006 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
53007 #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
53009 #elif defined(__WATCOMC__) && defined(__386__)
53010 #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
53011 #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
53012 #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
53013 extern __inline drflac_uint16 _watcom_bswap16(drflac_uint16);
53014 extern __inline drflac_uint32 _watcom_bswap32(drflac_uint32);
53015 extern __inline drflac_uint64 _watcom_bswap64(drflac_uint64);
53016 #pragma aux _watcom_bswap16 = \
53020 #pragma aux _watcom_bswap32 = \
53024 #pragma aux _watcom_bswap64 = \
53031 #ifndef DRFLAC_ASSERT
53032 #include <assert.h>
53033 #define DRFLAC_ASSERT(expression) assert(expression)
53035 #ifndef DRFLAC_MALLOC
53036 #define DRFLAC_MALLOC(sz) malloc((sz))
53038 #ifndef DRFLAC_REALLOC
53039 #define DRFLAC_REALLOC(p, sz) realloc((p), (sz))
53041 #ifndef DRFLAC_FREE
53042 #define DRFLAC_FREE(p) free((p))
53044 #ifndef DRFLAC_COPY_MEMORY
53045 #define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
53047 #ifndef DRFLAC_ZERO_MEMORY
53048 #define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
53050 #ifndef DRFLAC_ZERO_OBJECT
53051 #define DRFLAC_ZERO_OBJECT(p) DRFLAC_ZERO_MEMORY((p), sizeof(*(p)))
53053 #define DRFLAC_MAX_SIMD_VECTOR_SIZE 64
53054 typedef drflac_int32 drflac_result;
53055 #define DRFLAC_SUCCESS 0
53056 #define DRFLAC_ERROR -1
53057 #define DRFLAC_INVALID_ARGS -2
53058 #define DRFLAC_INVALID_OPERATION -3
53059 #define DRFLAC_OUT_OF_MEMORY -4
53060 #define DRFLAC_OUT_OF_RANGE -5
53061 #define DRFLAC_ACCESS_DENIED -6
53062 #define DRFLAC_DOES_NOT_EXIST -7
53063 #define DRFLAC_ALREADY_EXISTS -8
53064 #define DRFLAC_TOO_MANY_OPEN_FILES -9
53065 #define DRFLAC_INVALID_FILE -10
53066 #define DRFLAC_TOO_BIG -11
53067 #define DRFLAC_PATH_TOO_LONG -12
53068 #define DRFLAC_NAME_TOO_LONG -13
53069 #define DRFLAC_NOT_DIRECTORY -14
53070 #define DRFLAC_IS_DIRECTORY -15
53071 #define DRFLAC_DIRECTORY_NOT_EMPTY -16
53072 #define DRFLAC_END_OF_FILE -17
53073 #define DRFLAC_NO_SPACE -18
53074 #define DRFLAC_BUSY -19
53075 #define DRFLAC_IO_ERROR -20
53076 #define DRFLAC_INTERRUPT -21
53077 #define DRFLAC_UNAVAILABLE -22
53078 #define DRFLAC_ALREADY_IN_USE -23
53079 #define DRFLAC_BAD_ADDRESS -24
53080 #define DRFLAC_BAD_SEEK -25
53081 #define DRFLAC_BAD_PIPE -26
53082 #define DRFLAC_DEADLOCK -27
53083 #define DRFLAC_TOO_MANY_LINKS -28
53084 #define DRFLAC_NOT_IMPLEMENTED -29
53085 #define DRFLAC_NO_MESSAGE -30
53086 #define DRFLAC_BAD_MESSAGE -31
53087 #define DRFLAC_NO_DATA_AVAILABLE -32
53088 #define DRFLAC_INVALID_DATA -33
53089 #define DRFLAC_TIMEOUT -34
53090 #define DRFLAC_NO_NETWORK -35
53091 #define DRFLAC_NOT_UNIQUE -36
53092 #define DRFLAC_NOT_SOCKET -37
53093 #define DRFLAC_NO_ADDRESS -38
53094 #define DRFLAC_BAD_PROTOCOL -39
53095 #define DRFLAC_PROTOCOL_UNAVAILABLE -40
53096 #define DRFLAC_PROTOCOL_NOT_SUPPORTED -41
53097 #define DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED -42
53098 #define DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED -43
53099 #define DRFLAC_SOCKET_NOT_SUPPORTED -44
53100 #define DRFLAC_CONNECTION_RESET -45
53101 #define DRFLAC_ALREADY_CONNECTED -46
53102 #define DRFLAC_NOT_CONNECTED -47
53103 #define DRFLAC_CONNECTION_REFUSED -48
53104 #define DRFLAC_NO_HOST -49
53105 #define DRFLAC_IN_PROGRESS -50
53106 #define DRFLAC_CANCELLED -51
53107 #define DRFLAC_MEMORY_ALREADY_MAPPED -52
53108 #define DRFLAC_AT_END -53
53109 #define DRFLAC_CRC_MISMATCH -128
53110 #define DRFLAC_SUBFRAME_CONSTANT 0
53111 #define DRFLAC_SUBFRAME_VERBATIM 1
53112 #define DRFLAC_SUBFRAME_FIXED 8
53113 #define DRFLAC_SUBFRAME_LPC 32
53114 #define DRFLAC_SUBFRAME_RESERVED 255
53115 #define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0
53116 #define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1
53117 #define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0
53118 #define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8
53119 #define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9
53120 #define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10
53121 #define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
53122 DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision)
53125 *pMajor = DRFLAC_VERSION_MAJOR;
53128 *pMinor = DRFLAC_VERSION_MINOR;
53131 *pRevision = DRFLAC_VERSION_REVISION;
53134 DRFLAC_API const char* drflac_version_string(void)
53136 return DRFLAC_VERSION_STRING;
53138 #if defined(__has_feature)
53139 #if __has_feature(thread_sanitizer)
53140 #define DRFLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread")))
53142 #define DRFLAC_NO_THREAD_SANITIZE
53145 #define DRFLAC_NO_THREAD_SANITIZE
53147 #if defined(DRFLAC_HAS_LZCNT_INTRINSIC)
53148 static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE;
53150 #ifndef DRFLAC_NO_CPUID
53151 static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE;
53152 static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE;
53153 DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void)
53155 static drflac_bool32 isCPUCapsInitialized = DRFLAC_FALSE;
53156 if (!isCPUCapsInitialized) {
53157 #if defined(DRFLAC_HAS_LZCNT_INTRINSIC)
53159 drflac__cpuid(info, 0x80000001);
53160 drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0;
53162 drflac__gIsSSE2Supported = drflac_has_sse2();
53163 drflac__gIsSSE41Supported = drflac_has_sse41();
53164 isCPUCapsInitialized = DRFLAC_TRUE;
53168 static drflac_bool32 drflac__gIsNEONSupported = DRFLAC_FALSE;
53169 static DRFLAC_INLINE drflac_bool32 drflac__has_neon(void)
53171 #if defined(DRFLAC_SUPPORT_NEON)
53172 #if defined(DRFLAC_ARM) && !defined(DRFLAC_NO_NEON)
53173 #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
53174 return DRFLAC_TRUE;
53176 return DRFLAC_FALSE;
53179 return DRFLAC_FALSE;
53182 return DRFLAC_FALSE;
53185 DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void)
53187 drflac__gIsNEONSupported = drflac__has_neon();
53188 #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)
53189 drflac__gIsLZCNTSupported = DRFLAC_TRUE;
53193 static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian(void)
53195 #if defined(DRFLAC_X86) || defined(DRFLAC_X64)
53196 return DRFLAC_TRUE;
53197 #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
53198 return DRFLAC_TRUE;
53201 return (*(char*)&n) == 1;
53204 static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n)
53206 #ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC
53207 #if defined(_MSC_VER) && !defined(__clang__)
53208 return _byteswap_ushort(n);
53209 #elif defined(__GNUC__) || defined(__clang__)
53210 return __builtin_bswap16(n);
53211 #elif defined(__WATCOMC__) && defined(__386__)
53212 return _watcom_bswap16(n);
53214 #error "This compiler does not support the byte swap intrinsic."
53217 return ((n & 0xFF00) >> 8) |
53218 ((n & 0x00FF) << 8);
53221 static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n)
53223 #ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC
53224 #if defined(_MSC_VER) && !defined(__clang__)
53225 return _byteswap_ulong(n);
53226 #elif defined(__GNUC__) || defined(__clang__)
53227 #if defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRFLAC_64BIT)
53229 __asm__ __volatile__ (
53230 #if defined(DRFLAC_64BIT)
53231 "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n)
53233 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
53238 return __builtin_bswap32(n);
53240 #elif defined(__WATCOMC__) && defined(__386__)
53241 return _watcom_bswap32(n);
53243 #error "This compiler does not support the byte swap intrinsic."
53246 return ((n & 0xFF000000) >> 24) |
53247 ((n & 0x00FF0000) >> 8) |
53248 ((n & 0x0000FF00) << 8) |
53249 ((n & 0x000000FF) << 24);
53252 static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n)
53254 #ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC
53255 #if defined(_MSC_VER) && !defined(__clang__)
53256 return _byteswap_uint64(n);
53257 #elif defined(__GNUC__) || defined(__clang__)
53258 return __builtin_bswap64(n);
53259 #elif defined(__WATCOMC__) && defined(__386__)
53260 return _watcom_bswap64(n);
53262 #error "This compiler does not support the byte swap intrinsic."
53265 return ((n & ((drflac_uint64)0xFF000000 << 32)) >> 56) |
53266 ((n & ((drflac_uint64)0x00FF0000 << 32)) >> 40) |
53267 ((n & ((drflac_uint64)0x0000FF00 << 32)) >> 24) |
53268 ((n & ((drflac_uint64)0x000000FF << 32)) >> 8) |
53269 ((n & ((drflac_uint64)0xFF000000 )) << 8) |
53270 ((n & ((drflac_uint64)0x00FF0000 )) << 24) |
53271 ((n & ((drflac_uint64)0x0000FF00 )) << 40) |
53272 ((n & ((drflac_uint64)0x000000FF )) << 56);
53275 static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n)
53277 if (drflac__is_little_endian()) {
53278 return drflac__swap_endian_uint16(n);
53282 static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n)
53284 if (drflac__is_little_endian()) {
53285 return drflac__swap_endian_uint32(n);
53289 static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n)
53291 if (drflac__is_little_endian()) {
53292 return drflac__swap_endian_uint64(n);
53296 static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n)
53298 if (!drflac__is_little_endian()) {
53299 return drflac__swap_endian_uint32(n);
53303 static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n)
53305 drflac_uint32 result = 0;
53306 result |= (n & 0x7F000000) >> 3;
53307 result |= (n & 0x007F0000) >> 2;
53308 result |= (n & 0x00007F00) >> 1;
53309 result |= (n & 0x0000007F) >> 0;
53312 static drflac_uint8 drflac__crc8_table[] = {
53313 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
53314 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
53315 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
53316 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
53317 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
53318 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
53319 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
53320 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
53321 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
53322 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
53323 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
53324 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
53325 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
53326 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
53327 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
53328 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
53330 static drflac_uint16 drflac__crc16_table[] = {
53331 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
53332 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
53333 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,
53334 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
53335 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,
53336 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
53337 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,
53338 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
53339 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,
53340 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
53341 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,
53342 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
53343 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,
53344 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
53345 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,
53346 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,
53347 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,
53348 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
53349 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,
53350 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
53351 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,
53352 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
53353 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,
53354 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
53355 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,
53356 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
53357 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,
53358 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
53359 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,
53360 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
53361 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,
53362 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202
53364 static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data)
53366 return drflac__crc8_table[crc ^ data];
53368 static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count)
53370 #ifdef DR_FLAC_NO_CRC
53377 drflac_uint8 p = 0x07;
53378 for (int i = count-1; i >= 0; --i) {
53379 drflac_uint8 bit = (data & (1 << i)) >> i;
53381 crc = ((crc << 1) | bit) ^ p;
53383 crc = ((crc << 1) | bit);
53388 drflac_uint32 wholeBytes;
53389 drflac_uint32 leftoverBits;
53390 drflac_uint64 leftoverDataMask;
53391 static drflac_uint64 leftoverDataMaskTable[8] = {
53392 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
53394 DRFLAC_ASSERT(count <= 32);
53395 wholeBytes = count >> 3;
53396 leftoverBits = count - (wholeBytes*8);
53397 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
53398 switch (wholeBytes) {
53399 case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
53400 case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
53401 case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
53402 case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
53403 case 0: if (leftoverBits > 0) crc = (drflac_uint8)((crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]);
53409 static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data)
53411 return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data];
53413 static DRFLAC_INLINE drflac_uint16 drflac_crc16_cache(drflac_uint16 crc, drflac_cache_t data)
53415 #ifdef DRFLAC_64BIT
53416 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF));
53417 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF));
53418 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF));
53419 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF));
53421 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF));
53422 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF));
53423 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF));
53424 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF));
53427 static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount)
53431 #ifdef DRFLAC_64BIT
53432 case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF));
53433 case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF));
53434 case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF));
53435 case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF));
53437 case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF));
53438 case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF));
53439 case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF));
53440 case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF));
53445 static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count)
53447 #ifdef DR_FLAC_NO_CRC
53454 drflac_uint16 p = 0x8005;
53455 for (int i = count-1; i >= 0; --i) {
53456 drflac_uint16 bit = (data & (1ULL << i)) >> i;
53458 r = ((r << 1) | bit) ^ p;
53460 r = ((r << 1) | bit);
53465 drflac_uint32 wholeBytes;
53466 drflac_uint32 leftoverBits;
53467 drflac_uint64 leftoverDataMask;
53468 static drflac_uint64 leftoverDataMaskTable[8] = {
53469 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
53471 DRFLAC_ASSERT(count <= 64);
53472 wholeBytes = count >> 3;
53473 leftoverBits = count & 7;
53474 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
53475 switch (wholeBytes) {
53477 case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
53478 case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
53479 case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
53480 case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
53481 case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
53487 static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count)
53489 #ifdef DR_FLAC_NO_CRC
53495 drflac_uint32 wholeBytes;
53496 drflac_uint32 leftoverBits;
53497 drflac_uint64 leftoverDataMask;
53498 static drflac_uint64 leftoverDataMaskTable[8] = {
53499 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
53501 DRFLAC_ASSERT(count <= 64);
53502 wholeBytes = count >> 3;
53503 leftoverBits = count & 7;
53504 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
53505 switch (wholeBytes) {
53507 case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits)));
53508 case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits)));
53509 case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits)));
53510 case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits)));
53511 case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits)));
53512 case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits)));
53513 case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits)));
53514 case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits)));
53515 case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
53520 static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count)
53522 #ifdef DRFLAC_64BIT
53523 return drflac_crc16__64bit(crc, data, count);
53525 return drflac_crc16__32bit(crc, data, count);
53529 #ifdef DRFLAC_64BIT
53530 #define drflac__be2host__cache_line drflac__be2host_64
53532 #define drflac__be2host__cache_line drflac__be2host_32
53534 #define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache))
53535 #define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8)
53536 #define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits)
53537 #define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(drflac_cache_t)0) >> (_bitCount)))
53538 #define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount))
53539 #define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount))
53540 #define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)))
53541 #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)))
53542 #define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2))
53543 #define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0]))
53544 #define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line)
53545 #ifndef DR_FLAC_NO_CRC
53546 static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs)
53549 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
53551 static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs)
53553 if (bs->crc16CacheIgnoredBytes == 0) {
53554 bs->crc16 = drflac_crc16_cache(bs->crc16, bs->crc16Cache);
53556 bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes);
53557 bs->crc16CacheIgnoredBytes = 0;
53560 static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs)
53562 DRFLAC_ASSERT((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0);
53563 if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) {
53564 drflac__update_crc16(bs);
53566 bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes);
53567 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
53572 static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs)
53575 size_t alignedL1LineCount;
53576 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
53577 bs->cache = bs->cacheL2[bs->nextL2Line++];
53578 return DRFLAC_TRUE;
53580 if (bs->unalignedByteCount > 0) {
53581 return DRFLAC_FALSE;
53583 bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs));
53584 bs->nextL2Line = 0;
53585 if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) {
53586 bs->cache = bs->cacheL2[bs->nextL2Line++];
53587 return DRFLAC_TRUE;
53589 alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs);
53590 bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs));
53591 if (bs->unalignedByteCount > 0) {
53592 bs->unalignedCache = bs->cacheL2[alignedL1LineCount];
53594 if (alignedL1LineCount > 0) {
53595 size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount;
53597 for (i = alignedL1LineCount; i > 0; --i) {
53598 bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1];
53600 bs->nextL2Line = (drflac_uint32)offset;
53601 bs->cache = bs->cacheL2[bs->nextL2Line++];
53602 return DRFLAC_TRUE;
53604 bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs);
53605 return DRFLAC_FALSE;
53608 static drflac_bool32 drflac__reload_cache(drflac_bs* bs)
53611 #ifndef DR_FLAC_NO_CRC
53612 drflac__update_crc16(bs);
53614 if (drflac__reload_l1_cache_from_l2(bs)) {
53615 bs->cache = drflac__be2host__cache_line(bs->cache);
53616 bs->consumedBits = 0;
53617 #ifndef DR_FLAC_NO_CRC
53618 bs->crc16Cache = bs->cache;
53620 return DRFLAC_TRUE;
53622 bytesRead = bs->unalignedByteCount;
53623 if (bytesRead == 0) {
53624 bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs);
53625 return DRFLAC_FALSE;
53627 DRFLAC_ASSERT(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs));
53628 bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8;
53629 bs->cache = drflac__be2host__cache_line(bs->unalignedCache);
53630 bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_BITS_REMAINING(bs));
53631 bs->unalignedByteCount = 0;
53632 #ifndef DR_FLAC_NO_CRC
53633 bs->crc16Cache = bs->cache >> bs->consumedBits;
53634 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
53636 return DRFLAC_TRUE;
53638 static void drflac__reset_cache(drflac_bs* bs)
53640 bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs);
53641 bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs);
53643 bs->unalignedByteCount = 0;
53644 bs->unalignedCache = 0;
53645 #ifndef DR_FLAC_NO_CRC
53646 bs->crc16Cache = 0;
53647 bs->crc16CacheIgnoredBytes = 0;
53650 static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut)
53652 DRFLAC_ASSERT(bs != NULL);
53653 DRFLAC_ASSERT(pResultOut != NULL);
53654 DRFLAC_ASSERT(bitCount > 0);
53655 DRFLAC_ASSERT(bitCount <= 32);
53656 if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
53657 if (!drflac__reload_cache(bs)) {
53658 return DRFLAC_FALSE;
53661 if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
53662 #ifdef DRFLAC_64BIT
53663 *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
53664 bs->consumedBits += bitCount;
53665 bs->cache <<= bitCount;
53667 if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
53668 *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
53669 bs->consumedBits += bitCount;
53670 bs->cache <<= bitCount;
53672 *pResultOut = (drflac_uint32)bs->cache;
53673 bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs);
53677 return DRFLAC_TRUE;
53679 drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs);
53680 drflac_uint32 bitCountLo = bitCount - bitCountHi;
53681 drflac_uint32 resultHi;
53682 DRFLAC_ASSERT(bitCountHi > 0);
53683 DRFLAC_ASSERT(bitCountHi < 32);
53684 resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi);
53685 if (!drflac__reload_cache(bs)) {
53686 return DRFLAC_FALSE;
53688 *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo);
53689 bs->consumedBits += bitCountLo;
53690 bs->cache <<= bitCountLo;
53691 return DRFLAC_TRUE;
53694 static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult)
53696 drflac_uint32 result;
53697 DRFLAC_ASSERT(bs != NULL);
53698 DRFLAC_ASSERT(pResult != NULL);
53699 DRFLAC_ASSERT(bitCount > 0);
53700 DRFLAC_ASSERT(bitCount <= 32);
53701 if (!drflac__read_uint32(bs, bitCount, &result)) {
53702 return DRFLAC_FALSE;
53704 if (bitCount < 32) {
53705 drflac_uint32 signbit;
53706 signbit = ((result >> (bitCount-1)) & 0x01);
53707 result |= (~signbit + 1) << bitCount;
53709 *pResult = (drflac_int32)result;
53710 return DRFLAC_TRUE;
53712 #ifdef DRFLAC_64BIT
53713 static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut)
53715 drflac_uint32 resultHi;
53716 drflac_uint32 resultLo;
53717 DRFLAC_ASSERT(bitCount <= 64);
53718 DRFLAC_ASSERT(bitCount > 32);
53719 if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) {
53720 return DRFLAC_FALSE;
53722 if (!drflac__read_uint32(bs, 32, &resultLo)) {
53723 return DRFLAC_FALSE;
53725 *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo);
53726 return DRFLAC_TRUE;
53730 static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut)
53732 drflac_uint64 result;
53733 drflac_uint64 signbit;
53734 DRFLAC_ASSERT(bitCount <= 64);
53735 if (!drflac__read_uint64(bs, bitCount, &result)) {
53736 return DRFLAC_FALSE;
53738 signbit = ((result >> (bitCount-1)) & 0x01);
53739 result |= (~signbit + 1) << bitCount;
53740 *pResultOut = (drflac_int64)result;
53741 return DRFLAC_TRUE;
53744 static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult)
53746 drflac_uint32 result;
53747 DRFLAC_ASSERT(bs != NULL);
53748 DRFLAC_ASSERT(pResult != NULL);
53749 DRFLAC_ASSERT(bitCount > 0);
53750 DRFLAC_ASSERT(bitCount <= 16);
53751 if (!drflac__read_uint32(bs, bitCount, &result)) {
53752 return DRFLAC_FALSE;
53754 *pResult = (drflac_uint16)result;
53755 return DRFLAC_TRUE;
53758 static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult)
53760 drflac_int32 result;
53761 DRFLAC_ASSERT(bs != NULL);
53762 DRFLAC_ASSERT(pResult != NULL);
53763 DRFLAC_ASSERT(bitCount > 0);
53764 DRFLAC_ASSERT(bitCount <= 16);
53765 if (!drflac__read_int32(bs, bitCount, &result)) {
53766 return DRFLAC_FALSE;
53768 *pResult = (drflac_int16)result;
53769 return DRFLAC_TRUE;
53772 static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult)
53774 drflac_uint32 result;
53775 DRFLAC_ASSERT(bs != NULL);
53776 DRFLAC_ASSERT(pResult != NULL);
53777 DRFLAC_ASSERT(bitCount > 0);
53778 DRFLAC_ASSERT(bitCount <= 8);
53779 if (!drflac__read_uint32(bs, bitCount, &result)) {
53780 return DRFLAC_FALSE;
53782 *pResult = (drflac_uint8)result;
53783 return DRFLAC_TRUE;
53785 static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult)
53787 drflac_int32 result;
53788 DRFLAC_ASSERT(bs != NULL);
53789 DRFLAC_ASSERT(pResult != NULL);
53790 DRFLAC_ASSERT(bitCount > 0);
53791 DRFLAC_ASSERT(bitCount <= 8);
53792 if (!drflac__read_int32(bs, bitCount, &result)) {
53793 return DRFLAC_FALSE;
53795 *pResult = (drflac_int8)result;
53796 return DRFLAC_TRUE;
53798 static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek)
53800 if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
53801 bs->consumedBits += (drflac_uint32)bitsToSeek;
53802 bs->cache <<= bitsToSeek;
53803 return DRFLAC_TRUE;
53805 bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs);
53806 bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs);
53808 #ifdef DRFLAC_64BIT
53809 while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
53811 if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
53812 return DRFLAC_FALSE;
53814 bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs);
53817 while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
53819 if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
53820 return DRFLAC_FALSE;
53822 bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs);
53825 while (bitsToSeek >= 8) {
53827 if (!drflac__read_uint8(bs, 8, &bin)) {
53828 return DRFLAC_FALSE;
53832 if (bitsToSeek > 0) {
53834 if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) {
53835 return DRFLAC_FALSE;
53839 DRFLAC_ASSERT(bitsToSeek == 0);
53840 return DRFLAC_TRUE;
53843 static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs)
53845 DRFLAC_ASSERT(bs != NULL);
53846 if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
53847 return DRFLAC_FALSE;
53851 #ifndef DR_FLAC_NO_CRC
53852 drflac__reset_crc16(bs);
53854 if (!drflac__read_uint8(bs, 8, &hi)) {
53855 return DRFLAC_FALSE;
53859 if (!drflac__read_uint8(bs, 6, &lo)) {
53860 return DRFLAC_FALSE;
53863 return DRFLAC_TRUE;
53865 if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
53866 return DRFLAC_FALSE;
53872 #if defined(DRFLAC_HAS_LZCNT_INTRINSIC)
53873 #define DRFLAC_IMPLEMENT_CLZ_LZCNT
53875 #if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(__clang__)
53876 #define DRFLAC_IMPLEMENT_CLZ_MSVC
53878 #if defined(__WATCOMC__) && defined(__386__)
53879 #define DRFLAC_IMPLEMENT_CLZ_WATCOM
53881 static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x)
53884 static drflac_uint32 clz_table_4[] = {
53889 1, 1, 1, 1, 1, 1, 1, 1
53892 return sizeof(x)*8;
53894 n = clz_table_4[x >> (sizeof(x)*8 - 4)];
53896 #ifdef DRFLAC_64BIT
53897 if ((x & ((drflac_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; }
53898 if ((x & ((drflac_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; }
53899 if ((x & ((drflac_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; }
53900 if ((x & ((drflac_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; }
53902 if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; }
53903 if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; }
53904 if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; }
53906 n += clz_table_4[x >> (sizeof(x)*8 - 4)];
53910 #ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT
53911 static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void)
53913 #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)
53914 return DRFLAC_TRUE;
53916 #ifdef DRFLAC_HAS_LZCNT_INTRINSIC
53917 return drflac__gIsLZCNTSupported;
53919 return DRFLAC_FALSE;
53923 static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x)
53925 #if defined(_MSC_VER)
53926 #ifdef DRFLAC_64BIT
53927 return (drflac_uint32)__lzcnt64(x);
53929 return (drflac_uint32)__lzcnt(x);
53932 #if defined(__GNUC__) || defined(__clang__)
53933 #if defined(DRFLAC_X64)
53936 __asm__ __volatile__ (
53937 "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
53939 return (drflac_uint32)r;
53941 #elif defined(DRFLAC_X86)
53944 __asm__ __volatile__ (
53945 "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
53949 #elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(DRFLAC_64BIT)
53952 __asm__ __volatile__ (
53953 #if defined(DRFLAC_64BIT)
53954 "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x)
53956 "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x)
53963 return sizeof(x)*8;
53965 #ifdef DRFLAC_64BIT
53966 return (drflac_uint32)__builtin_clzll((drflac_uint64)x);
53968 return (drflac_uint32)__builtin_clzl((drflac_uint32)x);
53972 #error "This compiler does not support the lzcnt intrinsic."
53977 #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC
53978 #include <intrin.h>
53979 static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x)
53983 return sizeof(x)*8;
53985 #ifdef DRFLAC_64BIT
53986 _BitScanReverse64((unsigned long*)&n, x);
53988 _BitScanReverse((unsigned long*)&n, x);
53990 return sizeof(x)*8 - n - 1;
53993 #ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM
53994 static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32);
53995 #pragma aux drflac__clz_watcom = \
53998 parm [eax] nomemory \
54000 modify exact [eax] nomemory;
54002 static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x)
54004 #ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT
54005 if (drflac__is_lzcnt_supported()) {
54006 return drflac__clz_lzcnt(x);
54010 #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC
54011 return drflac__clz_msvc(x);
54012 #elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM)
54013 return (x == 0) ? sizeof(x)*8 : drflac__clz_watcom(x);
54015 return drflac__clz_software(x);
54019 static DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut)
54021 drflac_uint32 zeroCounter = 0;
54022 drflac_uint32 setBitOffsetPlus1;
54023 while (bs->cache == 0) {
54024 zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
54025 if (!drflac__reload_cache(bs)) {
54026 return DRFLAC_FALSE;
54029 setBitOffsetPlus1 = drflac__clz(bs->cache);
54030 setBitOffsetPlus1 += 1;
54031 bs->consumedBits += setBitOffsetPlus1;
54032 bs->cache <<= setBitOffsetPlus1;
54033 *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1;
54034 return DRFLAC_TRUE;
54036 static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart)
54038 DRFLAC_ASSERT(bs != NULL);
54039 DRFLAC_ASSERT(offsetFromStart > 0);
54040 if (offsetFromStart > 0x7FFFFFFF) {
54041 drflac_uint64 bytesRemaining = offsetFromStart;
54042 if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
54043 return DRFLAC_FALSE;
54045 bytesRemaining -= 0x7FFFFFFF;
54046 while (bytesRemaining > 0x7FFFFFFF) {
54047 if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
54048 return DRFLAC_FALSE;
54050 bytesRemaining -= 0x7FFFFFFF;
54052 if (bytesRemaining > 0) {
54053 if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) {
54054 return DRFLAC_FALSE;
54058 if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) {
54059 return DRFLAC_FALSE;
54062 drflac__reset_cache(bs);
54063 return DRFLAC_TRUE;
54065 static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut)
54068 drflac_uint64 result;
54069 drflac_uint8 utf8[7] = {0};
54072 DRFLAC_ASSERT(bs != NULL);
54073 DRFLAC_ASSERT(pNumberOut != NULL);
54074 DRFLAC_ASSERT(pCRCOut != NULL);
54076 if (!drflac__read_uint8(bs, 8, utf8)) {
54078 return DRFLAC_AT_END;
54080 crc = drflac_crc8(crc, utf8[0], 8);
54081 if ((utf8[0] & 0x80) == 0) {
54082 *pNumberOut = utf8[0];
54084 return DRFLAC_SUCCESS;
54086 if ((utf8[0] & 0xE0) == 0xC0) {
54088 } else if ((utf8[0] & 0xF0) == 0xE0) {
54090 } else if ((utf8[0] & 0xF8) == 0xF0) {
54092 } else if ((utf8[0] & 0xFC) == 0xF8) {
54094 } else if ((utf8[0] & 0xFE) == 0xFC) {
54096 } else if ((utf8[0] & 0xFF) == 0xFE) {
54100 return DRFLAC_CRC_MISMATCH;
54102 DRFLAC_ASSERT(byteCount > 1);
54103 result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1)));
54104 for (i = 1; i < byteCount; ++i) {
54105 if (!drflac__read_uint8(bs, 8, utf8 + i)) {
54107 return DRFLAC_AT_END;
54109 crc = drflac_crc8(crc, utf8[i], 8);
54110 result = (result << 6) | (utf8[i] & 0x3F);
54112 *pNumberOut = result;
54114 return DRFLAC_SUCCESS;
54116 static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
54118 drflac_int32 prediction = 0;
54119 DRFLAC_ASSERT(order <= 32);
54122 case 32: prediction += coefficients[31] * pDecodedSamples[-32];
54123 case 31: prediction += coefficients[30] * pDecodedSamples[-31];
54124 case 30: prediction += coefficients[29] * pDecodedSamples[-30];
54125 case 29: prediction += coefficients[28] * pDecodedSamples[-29];
54126 case 28: prediction += coefficients[27] * pDecodedSamples[-28];
54127 case 27: prediction += coefficients[26] * pDecodedSamples[-27];
54128 case 26: prediction += coefficients[25] * pDecodedSamples[-26];
54129 case 25: prediction += coefficients[24] * pDecodedSamples[-25];
54130 case 24: prediction += coefficients[23] * pDecodedSamples[-24];
54131 case 23: prediction += coefficients[22] * pDecodedSamples[-23];
54132 case 22: prediction += coefficients[21] * pDecodedSamples[-22];
54133 case 21: prediction += coefficients[20] * pDecodedSamples[-21];
54134 case 20: prediction += coefficients[19] * pDecodedSamples[-20];
54135 case 19: prediction += coefficients[18] * pDecodedSamples[-19];
54136 case 18: prediction += coefficients[17] * pDecodedSamples[-18];
54137 case 17: prediction += coefficients[16] * pDecodedSamples[-17];
54138 case 16: prediction += coefficients[15] * pDecodedSamples[-16];
54139 case 15: prediction += coefficients[14] * pDecodedSamples[-15];
54140 case 14: prediction += coefficients[13] * pDecodedSamples[-14];
54141 case 13: prediction += coefficients[12] * pDecodedSamples[-13];
54142 case 12: prediction += coefficients[11] * pDecodedSamples[-12];
54143 case 11: prediction += coefficients[10] * pDecodedSamples[-11];
54144 case 10: prediction += coefficients[ 9] * pDecodedSamples[-10];
54145 case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9];
54146 case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8];
54147 case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7];
54148 case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6];
54149 case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5];
54150 case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4];
54151 case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3];
54152 case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2];
54153 case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1];
54155 return (drflac_int32)(prediction >> shift);
54157 static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
54159 drflac_int64 prediction;
54160 DRFLAC_ASSERT(order <= 32);
54161 #ifndef DRFLAC_64BIT
54164 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
54165 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
54166 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
54167 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
54168 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
54169 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
54170 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
54171 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
54173 else if (order == 7)
54175 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
54176 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
54177 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
54178 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
54179 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
54180 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
54181 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
54183 else if (order == 3)
54185 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
54186 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
54187 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
54189 else if (order == 6)
54191 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
54192 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
54193 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
54194 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
54195 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
54196 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
54198 else if (order == 5)
54200 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
54201 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
54202 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
54203 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
54204 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
54206 else if (order == 4)
54208 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
54209 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
54210 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
54211 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
54213 else if (order == 12)
54215 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
54216 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
54217 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
54218 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
54219 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
54220 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
54221 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
54222 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
54223 prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
54224 prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
54225 prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
54226 prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12];
54228 else if (order == 2)
54230 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
54231 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
54233 else if (order == 1)
54235 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
54237 else if (order == 10)
54239 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
54240 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
54241 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
54242 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
54243 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
54244 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
54245 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
54246 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
54247 prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
54248 prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
54250 else if (order == 9)
54252 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
54253 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
54254 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
54255 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
54256 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
54257 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
54258 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
54259 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
54260 prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
54262 else if (order == 11)
54264 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
54265 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
54266 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
54267 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
54268 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
54269 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
54270 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
54271 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
54272 prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
54273 prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
54274 prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
54280 for (j = 0; j < (int)order; ++j) {
54281 prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1];
54285 #ifdef DRFLAC_64BIT
54289 case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32];
54290 case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31];
54291 case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30];
54292 case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29];
54293 case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28];
54294 case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27];
54295 case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26];
54296 case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25];
54297 case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24];
54298 case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23];
54299 case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22];
54300 case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21];
54301 case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20];
54302 case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19];
54303 case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18];
54304 case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17];
54305 case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16];
54306 case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15];
54307 case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14];
54308 case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13];
54309 case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12];
54310 case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
54311 case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10];
54312 case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9];
54313 case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8];
54314 case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7];
54315 case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6];
54316 case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5];
54317 case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4];
54318 case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3];
54319 case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2];
54320 case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1];
54323 return (drflac_int32)(prediction >> shift);
54326 static 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)
54329 DRFLAC_ASSERT(bs != NULL);
54330 DRFLAC_ASSERT(pSamplesOut != NULL);
54331 for (i = 0; i < count; ++i) {
54332 drflac_uint32 zeroCounter = 0;
54335 if (!drflac__read_uint8(bs, 1, &bit)) {
54336 return DRFLAC_FALSE;
54344 drflac_uint32 decodedRice;
54345 if (riceParam > 0) {
54346 if (!drflac__read_uint32(bs, riceParam, &decodedRice)) {
54347 return DRFLAC_FALSE;
54352 decodedRice |= (zeroCounter << riceParam);
54353 if ((decodedRice & 0x01)) {
54354 decodedRice = ~(decodedRice >> 1);
54356 decodedRice = (decodedRice >> 1);
54358 if (bitsPerSample+shift >= 32) {
54359 pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i);
54361 pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i);
54364 return DRFLAC_TRUE;
54368 static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
54370 drflac_uint32 zeroCounter = 0;
54371 drflac_uint32 decodedRice;
54374 if (!drflac__read_uint8(bs, 1, &bit)) {
54375 return DRFLAC_FALSE;
54383 if (riceParam > 0) {
54384 if (!drflac__read_uint32(bs, riceParam, &decodedRice)) {
54385 return DRFLAC_FALSE;
54390 *pZeroCounterOut = zeroCounter;
54391 *pRiceParamPartOut = decodedRice;
54392 return DRFLAC_TRUE;
54396 static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
54398 drflac_cache_t riceParamMask;
54399 drflac_uint32 zeroCounter;
54400 drflac_uint32 setBitOffsetPlus1;
54401 drflac_uint32 riceParamPart;
54402 drflac_uint32 riceLength;
54403 DRFLAC_ASSERT(riceParam > 0);
54404 riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam);
54406 while (bs->cache == 0) {
54407 zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
54408 if (!drflac__reload_cache(bs)) {
54409 return DRFLAC_FALSE;
54412 setBitOffsetPlus1 = drflac__clz(bs->cache);
54413 zeroCounter += setBitOffsetPlus1;
54414 setBitOffsetPlus1 += 1;
54415 riceLength = setBitOffsetPlus1 + riceParam;
54416 if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
54417 riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength));
54418 bs->consumedBits += riceLength;
54419 bs->cache <<= riceLength;
54421 drflac_uint32 bitCountLo;
54422 drflac_cache_t resultHi;
54423 bs->consumedBits += riceLength;
54424 bs->cache <<= setBitOffsetPlus1 & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1);
54425 bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs);
54426 resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam);
54427 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
54428 #ifndef DR_FLAC_NO_CRC
54429 drflac__update_crc16(bs);
54431 bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
54432 bs->consumedBits = 0;
54433 #ifndef DR_FLAC_NO_CRC
54434 bs->crc16Cache = bs->cache;
54437 if (!drflac__reload_cache(bs)) {
54438 return DRFLAC_FALSE;
54441 riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo));
54442 bs->consumedBits += bitCountLo;
54443 bs->cache <<= bitCountLo;
54445 pZeroCounterOut[0] = zeroCounter;
54446 pRiceParamPartOut[0] = riceParamPart;
54447 return DRFLAC_TRUE;
54450 static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
54452 drflac_uint32 riceParamPlus1 = riceParam + 1;
54453 drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1);
54454 drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
54455 drflac_cache_t bs_cache = bs->cache;
54456 drflac_uint32 bs_consumedBits = bs->consumedBits;
54457 drflac_uint32 lzcount = drflac__clz(bs_cache);
54458 if (lzcount < sizeof(bs_cache)*8) {
54459 pZeroCounterOut[0] = lzcount;
54460 extract_rice_param_part:
54461 bs_cache <<= lzcount;
54462 bs_consumedBits += lzcount;
54463 if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
54464 pRiceParamPartOut[0] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
54465 bs_cache <<= riceParamPlus1;
54466 bs_consumedBits += riceParamPlus1;
54468 drflac_uint32 riceParamPartHi;
54469 drflac_uint32 riceParamPartLo;
54470 drflac_uint32 riceParamPartLoBitCount;
54471 riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
54472 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
54473 DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
54474 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
54475 #ifndef DR_FLAC_NO_CRC
54476 drflac__update_crc16(bs);
54478 bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
54479 bs_consumedBits = riceParamPartLoBitCount;
54480 #ifndef DR_FLAC_NO_CRC
54481 bs->crc16Cache = bs_cache;
54484 if (!drflac__reload_cache(bs)) {
54485 return DRFLAC_FALSE;
54487 bs_cache = bs->cache;
54488 bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
54490 riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount)));
54491 pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo;
54492 bs_cache <<= riceParamPartLoBitCount;
54495 drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits);
54497 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
54498 #ifndef DR_FLAC_NO_CRC
54499 drflac__update_crc16(bs);
54501 bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
54502 bs_consumedBits = 0;
54503 #ifndef DR_FLAC_NO_CRC
54504 bs->crc16Cache = bs_cache;
54507 if (!drflac__reload_cache(bs)) {
54508 return DRFLAC_FALSE;
54510 bs_cache = bs->cache;
54511 bs_consumedBits = bs->consumedBits;
54513 lzcount = drflac__clz(bs_cache);
54514 zeroCounter += lzcount;
54515 if (lzcount < sizeof(bs_cache)*8) {
54519 pZeroCounterOut[0] = zeroCounter;
54520 goto extract_rice_param_part;
54522 bs->cache = bs_cache;
54523 bs->consumedBits = bs_consumedBits;
54524 return DRFLAC_TRUE;
54526 static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac_uint8 riceParam)
54528 drflac_uint32 riceParamPlus1 = riceParam + 1;
54529 drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
54530 drflac_cache_t bs_cache = bs->cache;
54531 drflac_uint32 bs_consumedBits = bs->consumedBits;
54532 drflac_uint32 lzcount = drflac__clz(bs_cache);
54533 if (lzcount < sizeof(bs_cache)*8) {
54534 extract_rice_param_part:
54535 bs_cache <<= lzcount;
54536 bs_consumedBits += lzcount;
54537 if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
54538 bs_cache <<= riceParamPlus1;
54539 bs_consumedBits += riceParamPlus1;
54541 drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
54542 DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
54543 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
54544 #ifndef DR_FLAC_NO_CRC
54545 drflac__update_crc16(bs);
54547 bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
54548 bs_consumedBits = riceParamPartLoBitCount;
54549 #ifndef DR_FLAC_NO_CRC
54550 bs->crc16Cache = bs_cache;
54553 if (!drflac__reload_cache(bs)) {
54554 return DRFLAC_FALSE;
54556 bs_cache = bs->cache;
54557 bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
54559 bs_cache <<= riceParamPartLoBitCount;
54563 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
54564 #ifndef DR_FLAC_NO_CRC
54565 drflac__update_crc16(bs);
54567 bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
54568 bs_consumedBits = 0;
54569 #ifndef DR_FLAC_NO_CRC
54570 bs->crc16Cache = bs_cache;
54573 if (!drflac__reload_cache(bs)) {
54574 return DRFLAC_FALSE;
54576 bs_cache = bs->cache;
54577 bs_consumedBits = bs->consumedBits;
54579 lzcount = drflac__clz(bs_cache);
54580 if (lzcount < sizeof(bs_cache)*8) {
54584 goto extract_rice_param_part;
54586 bs->cache = bs_cache;
54587 bs->consumedBits = bs_consumedBits;
54588 return DRFLAC_TRUE;
54590 static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorder(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
54592 drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
54593 drflac_uint32 zeroCountPart0;
54594 drflac_uint32 riceParamPart0;
54595 drflac_uint32 riceParamMask;
54597 DRFLAC_ASSERT(bs != NULL);
54598 DRFLAC_ASSERT(pSamplesOut != NULL);
54599 (void)bitsPerSample;
54602 (void)coefficients;
54603 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
54605 while (i < count) {
54606 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
54607 return DRFLAC_FALSE;
54609 riceParamPart0 &= riceParamMask;
54610 riceParamPart0 |= (zeroCountPart0 << riceParam);
54611 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
54612 pSamplesOut[i] = riceParamPart0;
54615 return DRFLAC_TRUE;
54617 static 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)
54619 drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
54620 drflac_uint32 zeroCountPart0 = 0;
54621 drflac_uint32 zeroCountPart1 = 0;
54622 drflac_uint32 zeroCountPart2 = 0;
54623 drflac_uint32 zeroCountPart3 = 0;
54624 drflac_uint32 riceParamPart0 = 0;
54625 drflac_uint32 riceParamPart1 = 0;
54626 drflac_uint32 riceParamPart2 = 0;
54627 drflac_uint32 riceParamPart3 = 0;
54628 drflac_uint32 riceParamMask;
54629 const drflac_int32* pSamplesOutEnd;
54631 DRFLAC_ASSERT(bs != NULL);
54632 DRFLAC_ASSERT(pSamplesOut != NULL);
54634 return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
54636 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
54637 pSamplesOutEnd = pSamplesOut + (count & ~3);
54638 if (bitsPerSample+shift > 32) {
54639 while (pSamplesOut < pSamplesOutEnd) {
54640 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
54641 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
54642 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
54643 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
54644 return DRFLAC_FALSE;
54646 riceParamPart0 &= riceParamMask;
54647 riceParamPart1 &= riceParamMask;
54648 riceParamPart2 &= riceParamMask;
54649 riceParamPart3 &= riceParamMask;
54650 riceParamPart0 |= (zeroCountPart0 << riceParam);
54651 riceParamPart1 |= (zeroCountPart1 << riceParam);
54652 riceParamPart2 |= (zeroCountPart2 << riceParam);
54653 riceParamPart3 |= (zeroCountPart3 << riceParam);
54654 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
54655 riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
54656 riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
54657 riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
54658 pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0);
54659 pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 1);
54660 pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 2);
54661 pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 3);
54665 while (pSamplesOut < pSamplesOutEnd) {
54666 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
54667 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
54668 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
54669 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
54670 return DRFLAC_FALSE;
54672 riceParamPart0 &= riceParamMask;
54673 riceParamPart1 &= riceParamMask;
54674 riceParamPart2 &= riceParamMask;
54675 riceParamPart3 &= riceParamMask;
54676 riceParamPart0 |= (zeroCountPart0 << riceParam);
54677 riceParamPart1 |= (zeroCountPart1 << riceParam);
54678 riceParamPart2 |= (zeroCountPart2 << riceParam);
54679 riceParamPart3 |= (zeroCountPart3 << riceParam);
54680 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
54681 riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
54682 riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
54683 riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
54684 pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
54685 pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 1);
54686 pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 2);
54687 pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 3);
54692 while (i < count) {
54693 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
54694 return DRFLAC_FALSE;
54696 riceParamPart0 &= riceParamMask;
54697 riceParamPart0 |= (zeroCountPart0 << riceParam);
54698 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
54699 if (bitsPerSample+shift > 32) {
54700 pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0);
54702 pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
54707 return DRFLAC_TRUE;
54709 #if defined(DRFLAC_SUPPORT_SSE2)
54710 static DRFLAC_INLINE __m128i drflac__mm_packs_interleaved_epi32(__m128i a, __m128i b)
54713 r = _mm_packs_epi32(a, b);
54714 r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0));
54715 r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));
54716 r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));
54720 #if defined(DRFLAC_SUPPORT_SSE41)
54721 static DRFLAC_INLINE __m128i drflac__mm_not_si128(__m128i a)
54723 return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128()));
54725 static DRFLAC_INLINE __m128i drflac__mm_hadd_epi32(__m128i x)
54727 __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));
54728 __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2));
54729 return _mm_add_epi32(x64, x32);
54731 static DRFLAC_INLINE __m128i drflac__mm_hadd_epi64(__m128i x)
54733 return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));
54735 static DRFLAC_INLINE __m128i drflac__mm_srai_epi64(__m128i x, int count)
54737 __m128i lo = _mm_srli_epi64(x, count);
54738 __m128i hi = _mm_srai_epi32(x, count);
54739 hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0));
54740 return _mm_or_si128(lo, hi);
54742 static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
54745 drflac_uint32 riceParamMask;
54746 drflac_int32* pDecodedSamples = pSamplesOut;
54747 drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
54748 drflac_uint32 zeroCountParts0 = 0;
54749 drflac_uint32 zeroCountParts1 = 0;
54750 drflac_uint32 zeroCountParts2 = 0;
54751 drflac_uint32 zeroCountParts3 = 0;
54752 drflac_uint32 riceParamParts0 = 0;
54753 drflac_uint32 riceParamParts1 = 0;
54754 drflac_uint32 riceParamParts2 = 0;
54755 drflac_uint32 riceParamParts3 = 0;
54756 __m128i coefficients128_0;
54757 __m128i coefficients128_4;
54758 __m128i coefficients128_8;
54759 __m128i samples128_0;
54760 __m128i samples128_4;
54761 __m128i samples128_8;
54762 __m128i riceParamMask128;
54763 const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
54764 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
54765 riceParamMask128 = _mm_set1_epi32(riceParamMask);
54766 coefficients128_0 = _mm_setzero_si128();
54767 coefficients128_4 = _mm_setzero_si128();
54768 coefficients128_8 = _mm_setzero_si128();
54769 samples128_0 = _mm_setzero_si128();
54770 samples128_4 = _mm_setzero_si128();
54771 samples128_8 = _mm_setzero_si128();
54774 int runningOrder = order;
54775 if (runningOrder >= 4) {
54776 coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));
54777 samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4));
54780 switch (runningOrder) {
54781 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;
54782 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;
54783 case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break;
54787 if (runningOrder >= 4) {
54788 coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));
54789 samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8));
54792 switch (runningOrder) {
54793 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;
54794 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;
54795 case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break;
54799 if (runningOrder == 4) {
54800 coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));
54801 samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12));
54804 switch (runningOrder) {
54805 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;
54806 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;
54807 case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break;
54811 coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));
54812 coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));
54813 coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));
54818 case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12];
54819 case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11];
54820 case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10];
54821 case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9];
54822 case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8];
54823 case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7];
54824 case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6];
54825 case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5];
54826 case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4];
54827 case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3];
54828 case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2];
54829 case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1];
54832 while (pDecodedSamples < pDecodedSamplesEnd) {
54833 __m128i prediction128;
54834 __m128i zeroCountPart128;
54835 __m128i riceParamPart128;
54836 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
54837 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
54838 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
54839 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
54840 return DRFLAC_FALSE;
54842 zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
54843 riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
54844 riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
54845 riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
54846 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)));
54848 for (i = 0; i < 4; i += 1) {
54849 prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0);
54850 prediction128 = drflac__mm_hadd_epi32(prediction128);
54851 prediction128 = _mm_srai_epi32(prediction128, shift);
54852 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
54853 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
54854 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
54856 } else if (order <= 8) {
54857 for (i = 0; i < 4; i += 1) {
54858 prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4);
54859 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0));
54860 prediction128 = drflac__mm_hadd_epi32(prediction128);
54861 prediction128 = _mm_srai_epi32(prediction128, shift);
54862 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
54863 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
54864 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
54865 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
54868 for (i = 0; i < 4; i += 1) {
54869 prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8);
54870 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4));
54871 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0));
54872 prediction128 = drflac__mm_hadd_epi32(prediction128);
54873 prediction128 = _mm_srai_epi32(prediction128, shift);
54874 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
54875 samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4);
54876 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
54877 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
54878 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
54881 _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);
54882 pDecodedSamples += 4;
54885 while (i < (int)count) {
54886 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
54887 return DRFLAC_FALSE;
54889 riceParamParts0 &= riceParamMask;
54890 riceParamParts0 |= (zeroCountParts0 << riceParam);
54891 riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
54892 pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);
54894 pDecodedSamples += 1;
54896 return DRFLAC_TRUE;
54898 static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
54901 drflac_uint32 riceParamMask;
54902 drflac_int32* pDecodedSamples = pSamplesOut;
54903 drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
54904 drflac_uint32 zeroCountParts0 = 0;
54905 drflac_uint32 zeroCountParts1 = 0;
54906 drflac_uint32 zeroCountParts2 = 0;
54907 drflac_uint32 zeroCountParts3 = 0;
54908 drflac_uint32 riceParamParts0 = 0;
54909 drflac_uint32 riceParamParts1 = 0;
54910 drflac_uint32 riceParamParts2 = 0;
54911 drflac_uint32 riceParamParts3 = 0;
54912 __m128i coefficients128_0;
54913 __m128i coefficients128_4;
54914 __m128i coefficients128_8;
54915 __m128i samples128_0;
54916 __m128i samples128_4;
54917 __m128i samples128_8;
54918 __m128i prediction128;
54919 __m128i riceParamMask128;
54920 const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
54921 DRFLAC_ASSERT(order <= 12);
54922 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
54923 riceParamMask128 = _mm_set1_epi32(riceParamMask);
54924 prediction128 = _mm_setzero_si128();
54925 coefficients128_0 = _mm_setzero_si128();
54926 coefficients128_4 = _mm_setzero_si128();
54927 coefficients128_8 = _mm_setzero_si128();
54928 samples128_0 = _mm_setzero_si128();
54929 samples128_4 = _mm_setzero_si128();
54930 samples128_8 = _mm_setzero_si128();
54933 int runningOrder = order;
54934 if (runningOrder >= 4) {
54935 coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));
54936 samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4));
54939 switch (runningOrder) {
54940 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;
54941 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;
54942 case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break;
54946 if (runningOrder >= 4) {
54947 coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));
54948 samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8));
54951 switch (runningOrder) {
54952 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;
54953 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;
54954 case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break;
54958 if (runningOrder == 4) {
54959 coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));
54960 samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12));
54963 switch (runningOrder) {
54964 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;
54965 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;
54966 case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break;
54970 coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));
54971 coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));
54972 coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));
54977 case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12];
54978 case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11];
54979 case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10];
54980 case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9];
54981 case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8];
54982 case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7];
54983 case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6];
54984 case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5];
54985 case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4];
54986 case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3];
54987 case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2];
54988 case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1];
54991 while (pDecodedSamples < pDecodedSamplesEnd) {
54992 __m128i zeroCountPart128;
54993 __m128i riceParamPart128;
54994 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
54995 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
54996 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
54997 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
54998 return DRFLAC_FALSE;
55000 zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
55001 riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
55002 riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
55003 riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
55004 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)));
55005 for (i = 0; i < 4; i += 1) {
55006 prediction128 = _mm_xor_si128(prediction128, prediction128);
55010 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))));
55012 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))));
55014 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))));
55016 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))));
55018 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))));
55020 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))));
55022 prediction128 = drflac__mm_hadd_epi64(prediction128);
55023 prediction128 = drflac__mm_srai_epi64(prediction128, shift);
55024 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
55025 samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4);
55026 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
55027 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
55028 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
55030 _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);
55031 pDecodedSamples += 4;
55034 while (i < (int)count) {
55035 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
55036 return DRFLAC_FALSE;
55038 riceParamParts0 &= riceParamMask;
55039 riceParamParts0 |= (zeroCountParts0 << riceParam);
55040 riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
55041 pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);
55043 pDecodedSamples += 1;
55045 return DRFLAC_TRUE;
55047 static 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)
55049 DRFLAC_ASSERT(bs != NULL);
55050 DRFLAC_ASSERT(pSamplesOut != NULL);
55051 if (order > 0 && order <= 12) {
55052 if (bitsPerSample+shift > 32) {
55053 return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, order, shift, coefficients, pSamplesOut);
55055 return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, order, shift, coefficients, pSamplesOut);
55058 return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
55062 #if defined(DRFLAC_SUPPORT_NEON)
55063 static DRFLAC_INLINE void drflac__vst2q_s32(drflac_int32* p, int32x4x2_t x)
55065 vst1q_s32(p+0, x.val[0]);
55066 vst1q_s32(p+4, x.val[1]);
55068 static DRFLAC_INLINE void drflac__vst2q_u32(drflac_uint32* p, uint32x4x2_t x)
55070 vst1q_u32(p+0, x.val[0]);
55071 vst1q_u32(p+4, x.val[1]);
55073 static DRFLAC_INLINE void drflac__vst2q_f32(float* p, float32x4x2_t x)
55075 vst1q_f32(p+0, x.val[0]);
55076 vst1q_f32(p+4, x.val[1]);
55078 static DRFLAC_INLINE void drflac__vst2q_s16(drflac_int16* p, int16x4x2_t x)
55080 vst1q_s16(p, vcombine_s16(x.val[0], x.val[1]));
55082 static DRFLAC_INLINE void drflac__vst2q_u16(drflac_uint16* p, uint16x4x2_t x)
55084 vst1q_u16(p, vcombine_u16(x.val[0], x.val[1]));
55086 static DRFLAC_INLINE int32x4_t drflac__vdupq_n_s32x4(drflac_int32 x3, drflac_int32 x2, drflac_int32 x1, drflac_int32 x0)
55093 return vld1q_s32(x);
55095 static DRFLAC_INLINE int32x4_t drflac__valignrq_s32_1(int32x4_t a, int32x4_t b)
55097 return vextq_s32(b, a, 1);
55099 static DRFLAC_INLINE uint32x4_t drflac__valignrq_u32_1(uint32x4_t a, uint32x4_t b)
55101 return vextq_u32(b, a, 1);
55103 static DRFLAC_INLINE int32x2_t drflac__vhaddq_s32(int32x4_t x)
55105 int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x));
55106 return vpadd_s32(r, r);
55108 static DRFLAC_INLINE int64x1_t drflac__vhaddq_s64(int64x2_t x)
55110 return vadd_s64(vget_high_s64(x), vget_low_s64(x));
55112 static DRFLAC_INLINE int32x4_t drflac__vrevq_s32(int32x4_t x)
55114 return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x)));
55116 static DRFLAC_INLINE int32x4_t drflac__vnotq_s32(int32x4_t x)
55118 return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF));
55120 static DRFLAC_INLINE uint32x4_t drflac__vnotq_u32(uint32x4_t x)
55122 return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF));
55124 static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
55127 drflac_uint32 riceParamMask;
55128 drflac_int32* pDecodedSamples = pSamplesOut;
55129 drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
55130 drflac_uint32 zeroCountParts[4];
55131 drflac_uint32 riceParamParts[4];
55132 int32x4_t coefficients128_0;
55133 int32x4_t coefficients128_4;
55134 int32x4_t coefficients128_8;
55135 int32x4_t samples128_0;
55136 int32x4_t samples128_4;
55137 int32x4_t samples128_8;
55138 uint32x4_t riceParamMask128;
55139 int32x4_t riceParam128;
55142 const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
55143 riceParamMask = ~((~0UL) << riceParam);
55144 riceParamMask128 = vdupq_n_u32(riceParamMask);
55145 riceParam128 = vdupq_n_s32(riceParam);
55146 shift64 = vdup_n_s32(-shift);
55147 one128 = vdupq_n_u32(1);
55149 int runningOrder = order;
55150 drflac_int32 tempC[4] = {0, 0, 0, 0};
55151 drflac_int32 tempS[4] = {0, 0, 0, 0};
55152 if (runningOrder >= 4) {
55153 coefficients128_0 = vld1q_s32(coefficients + 0);
55154 samples128_0 = vld1q_s32(pSamplesOut - 4);
55157 switch (runningOrder) {
55158 case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3];
55159 case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2];
55160 case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1];
55162 coefficients128_0 = vld1q_s32(tempC);
55163 samples128_0 = vld1q_s32(tempS);
55166 if (runningOrder >= 4) {
55167 coefficients128_4 = vld1q_s32(coefficients + 4);
55168 samples128_4 = vld1q_s32(pSamplesOut - 8);
55171 switch (runningOrder) {
55172 case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7];
55173 case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6];
55174 case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5];
55176 coefficients128_4 = vld1q_s32(tempC);
55177 samples128_4 = vld1q_s32(tempS);
55180 if (runningOrder == 4) {
55181 coefficients128_8 = vld1q_s32(coefficients + 8);
55182 samples128_8 = vld1q_s32(pSamplesOut - 12);
55185 switch (runningOrder) {
55186 case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11];
55187 case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10];
55188 case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9];
55190 coefficients128_8 = vld1q_s32(tempC);
55191 samples128_8 = vld1q_s32(tempS);
55194 coefficients128_0 = drflac__vrevq_s32(coefficients128_0);
55195 coefficients128_4 = drflac__vrevq_s32(coefficients128_4);
55196 coefficients128_8 = drflac__vrevq_s32(coefficients128_8);
55198 while (pDecodedSamples < pDecodedSamplesEnd) {
55199 int32x4_t prediction128;
55200 int32x2_t prediction64;
55201 uint32x4_t zeroCountPart128;
55202 uint32x4_t riceParamPart128;
55203 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||
55204 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||
55205 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||
55206 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {
55207 return DRFLAC_FALSE;
55209 zeroCountPart128 = vld1q_u32(zeroCountParts);
55210 riceParamPart128 = vld1q_u32(riceParamParts);
55211 riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);
55212 riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));
55213 riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));
55215 for (i = 0; i < 4; i += 1) {
55216 prediction128 = vmulq_s32(coefficients128_0, samples128_0);
55217 prediction64 = drflac__vhaddq_s32(prediction128);
55218 prediction64 = vshl_s32(prediction64, shift64);
55219 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
55220 samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
55221 riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
55223 } else if (order <= 8) {
55224 for (i = 0; i < 4; i += 1) {
55225 prediction128 = vmulq_s32(coefficients128_4, samples128_4);
55226 prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0);
55227 prediction64 = drflac__vhaddq_s32(prediction128);
55228 prediction64 = vshl_s32(prediction64, shift64);
55229 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
55230 samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4);
55231 samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
55232 riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
55235 for (i = 0; i < 4; i += 1) {
55236 prediction128 = vmulq_s32(coefficients128_8, samples128_8);
55237 prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4);
55238 prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0);
55239 prediction64 = drflac__vhaddq_s32(prediction128);
55240 prediction64 = vshl_s32(prediction64, shift64);
55241 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
55242 samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8);
55243 samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4);
55244 samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
55245 riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
55248 vst1q_s32(pDecodedSamples, samples128_0);
55249 pDecodedSamples += 4;
55252 while (i < (int)count) {
55253 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {
55254 return DRFLAC_FALSE;
55256 riceParamParts[0] &= riceParamMask;
55257 riceParamParts[0] |= (zeroCountParts[0] << riceParam);
55258 riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];
55259 pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);
55261 pDecodedSamples += 1;
55263 return DRFLAC_TRUE;
55265 static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
55268 drflac_uint32 riceParamMask;
55269 drflac_int32* pDecodedSamples = pSamplesOut;
55270 drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
55271 drflac_uint32 zeroCountParts[4];
55272 drflac_uint32 riceParamParts[4];
55273 int32x4_t coefficients128_0;
55274 int32x4_t coefficients128_4;
55275 int32x4_t coefficients128_8;
55276 int32x4_t samples128_0;
55277 int32x4_t samples128_4;
55278 int32x4_t samples128_8;
55279 uint32x4_t riceParamMask128;
55280 int32x4_t riceParam128;
55283 const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
55284 riceParamMask = ~((~0UL) << riceParam);
55285 riceParamMask128 = vdupq_n_u32(riceParamMask);
55286 riceParam128 = vdupq_n_s32(riceParam);
55287 shift64 = vdup_n_s64(-shift);
55288 one128 = vdupq_n_u32(1);
55290 int runningOrder = order;
55291 drflac_int32 tempC[4] = {0, 0, 0, 0};
55292 drflac_int32 tempS[4] = {0, 0, 0, 0};
55293 if (runningOrder >= 4) {
55294 coefficients128_0 = vld1q_s32(coefficients + 0);
55295 samples128_0 = vld1q_s32(pSamplesOut - 4);
55298 switch (runningOrder) {
55299 case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3];
55300 case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2];
55301 case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1];
55303 coefficients128_0 = vld1q_s32(tempC);
55304 samples128_0 = vld1q_s32(tempS);
55307 if (runningOrder >= 4) {
55308 coefficients128_4 = vld1q_s32(coefficients + 4);
55309 samples128_4 = vld1q_s32(pSamplesOut - 8);
55312 switch (runningOrder) {
55313 case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7];
55314 case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6];
55315 case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5];
55317 coefficients128_4 = vld1q_s32(tempC);
55318 samples128_4 = vld1q_s32(tempS);
55321 if (runningOrder == 4) {
55322 coefficients128_8 = vld1q_s32(coefficients + 8);
55323 samples128_8 = vld1q_s32(pSamplesOut - 12);
55326 switch (runningOrder) {
55327 case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11];
55328 case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10];
55329 case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9];
55331 coefficients128_8 = vld1q_s32(tempC);
55332 samples128_8 = vld1q_s32(tempS);
55335 coefficients128_0 = drflac__vrevq_s32(coefficients128_0);
55336 coefficients128_4 = drflac__vrevq_s32(coefficients128_4);
55337 coefficients128_8 = drflac__vrevq_s32(coefficients128_8);
55339 while (pDecodedSamples < pDecodedSamplesEnd) {
55340 int64x2_t prediction128;
55341 uint32x4_t zeroCountPart128;
55342 uint32x4_t riceParamPart128;
55343 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||
55344 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||
55345 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||
55346 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {
55347 return DRFLAC_FALSE;
55349 zeroCountPart128 = vld1q_u32(zeroCountParts);
55350 riceParamPart128 = vld1q_u32(riceParamParts);
55351 riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);
55352 riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));
55353 riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));
55354 for (i = 0; i < 4; i += 1) {
55355 int64x1_t prediction64;
55356 prediction128 = veorq_s64(prediction128, prediction128);
55360 case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8)));
55362 case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8)));
55364 case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4)));
55366 case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4)));
55368 case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0)));
55370 case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0)));
55372 prediction64 = drflac__vhaddq_s64(prediction128);
55373 prediction64 = vshl_s64(prediction64, shift64);
55374 prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0)));
55375 samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8);
55376 samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4);
55377 samples128_0 = drflac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0);
55378 riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
55380 vst1q_s32(pDecodedSamples, samples128_0);
55381 pDecodedSamples += 4;
55384 while (i < (int)count) {
55385 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {
55386 return DRFLAC_FALSE;
55388 riceParamParts[0] &= riceParamMask;
55389 riceParamParts[0] |= (zeroCountParts[0] << riceParam);
55390 riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];
55391 pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);
55393 pDecodedSamples += 1;
55395 return DRFLAC_TRUE;
55397 static 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)
55399 DRFLAC_ASSERT(bs != NULL);
55400 DRFLAC_ASSERT(pSamplesOut != NULL);
55401 if (order > 0 && order <= 12) {
55402 if (bitsPerSample+shift > 32) {
55403 return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, order, shift, coefficients, pSamplesOut);
55405 return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, order, shift, coefficients, pSamplesOut);
55408 return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
55412 static 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)
55414 #if defined(DRFLAC_SUPPORT_SSE41)
55415 if (drflac__gIsSSE41Supported) {
55416 return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
55418 #elif defined(DRFLAC_SUPPORT_NEON)
55419 if (drflac__gIsNEONSupported) {
55420 return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
55425 return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
55427 return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
55431 static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam)
55434 DRFLAC_ASSERT(bs != NULL);
55435 for (i = 0; i < count; ++i) {
55436 if (!drflac__seek_rice_parts(bs, riceParam)) {
55437 return DRFLAC_FALSE;
55440 return DRFLAC_TRUE;
55442 static 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)
55445 DRFLAC_ASSERT(bs != NULL);
55446 DRFLAC_ASSERT(unencodedBitsPerSample <= 31);
55447 DRFLAC_ASSERT(pSamplesOut != NULL);
55448 for (i = 0; i < count; ++i) {
55449 if (unencodedBitsPerSample > 0) {
55450 if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) {
55451 return DRFLAC_FALSE;
55454 pSamplesOut[i] = 0;
55456 if (bitsPerSample >= 24) {
55457 pSamplesOut[i] += drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i);
55459 pSamplesOut[i] += drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i);
55462 return DRFLAC_TRUE;
55464 static 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)
55466 drflac_uint8 residualMethod;
55467 drflac_uint8 partitionOrder;
55468 drflac_uint32 samplesInPartition;
55469 drflac_uint32 partitionsRemaining;
55470 DRFLAC_ASSERT(bs != NULL);
55471 DRFLAC_ASSERT(blockSize != 0);
55472 DRFLAC_ASSERT(pDecodedSamples != NULL);
55473 if (!drflac__read_uint8(bs, 2, &residualMethod)) {
55474 return DRFLAC_FALSE;
55476 if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
55477 return DRFLAC_FALSE;
55479 pDecodedSamples += order;
55480 if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
55481 return DRFLAC_FALSE;
55483 if (partitionOrder > 8) {
55484 return DRFLAC_FALSE;
55486 if ((blockSize / (1 << partitionOrder)) < order) {
55487 return DRFLAC_FALSE;
55489 samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
55490 partitionsRemaining = (1 << partitionOrder);
55492 drflac_uint8 riceParam = 0;
55493 if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
55494 if (!drflac__read_uint8(bs, 4, &riceParam)) {
55495 return DRFLAC_FALSE;
55497 if (riceParam == 15) {
55500 } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
55501 if (!drflac__read_uint8(bs, 5, &riceParam)) {
55502 return DRFLAC_FALSE;
55504 if (riceParam == 31) {
55508 if (riceParam != 0xFF) {
55509 if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) {
55510 return DRFLAC_FALSE;
55513 drflac_uint8 unencodedBitsPerSample = 0;
55514 if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
55515 return DRFLAC_FALSE;
55517 if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, order, shift, coefficients, pDecodedSamples)) {
55518 return DRFLAC_FALSE;
55521 pDecodedSamples += samplesInPartition;
55522 if (partitionsRemaining == 1) {
55525 partitionsRemaining -= 1;
55526 if (partitionOrder != 0) {
55527 samplesInPartition = blockSize / (1 << partitionOrder);
55530 return DRFLAC_TRUE;
55532 static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order)
55534 drflac_uint8 residualMethod;
55535 drflac_uint8 partitionOrder;
55536 drflac_uint32 samplesInPartition;
55537 drflac_uint32 partitionsRemaining;
55538 DRFLAC_ASSERT(bs != NULL);
55539 DRFLAC_ASSERT(blockSize != 0);
55540 if (!drflac__read_uint8(bs, 2, &residualMethod)) {
55541 return DRFLAC_FALSE;
55543 if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
55544 return DRFLAC_FALSE;
55546 if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
55547 return DRFLAC_FALSE;
55549 if (partitionOrder > 8) {
55550 return DRFLAC_FALSE;
55552 if ((blockSize / (1 << partitionOrder)) <= order) {
55553 return DRFLAC_FALSE;
55555 samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
55556 partitionsRemaining = (1 << partitionOrder);
55559 drflac_uint8 riceParam = 0;
55560 if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
55561 if (!drflac__read_uint8(bs, 4, &riceParam)) {
55562 return DRFLAC_FALSE;
55564 if (riceParam == 15) {
55567 } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
55568 if (!drflac__read_uint8(bs, 5, &riceParam)) {
55569 return DRFLAC_FALSE;
55571 if (riceParam == 31) {
55575 if (riceParam != 0xFF) {
55576 if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) {
55577 return DRFLAC_FALSE;
55580 drflac_uint8 unencodedBitsPerSample = 0;
55581 if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
55582 return DRFLAC_FALSE;
55584 if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) {
55585 return DRFLAC_FALSE;
55588 if (partitionsRemaining == 1) {
55591 partitionsRemaining -= 1;
55592 samplesInPartition = blockSize / (1 << partitionOrder);
55594 return DRFLAC_TRUE;
55596 static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples)
55599 drflac_int32 sample;
55600 if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) {
55601 return DRFLAC_FALSE;
55603 for (i = 0; i < blockSize; ++i) {
55604 pDecodedSamples[i] = sample;
55606 return DRFLAC_TRUE;
55608 static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples)
55611 for (i = 0; i < blockSize; ++i) {
55612 drflac_int32 sample;
55613 if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) {
55614 return DRFLAC_FALSE;
55616 pDecodedSamples[i] = sample;
55618 return DRFLAC_TRUE;
55620 static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples)
55623 static drflac_int32 lpcCoefficientsTable[5][4] = {
55630 for (i = 0; i < lpcOrder; ++i) {
55631 drflac_int32 sample;
55632 if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) {
55633 return DRFLAC_FALSE;
55635 pDecodedSamples[i] = sample;
55637 if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) {
55638 return DRFLAC_FALSE;
55640 return DRFLAC_TRUE;
55642 static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples)
55645 drflac_uint8 lpcPrecision;
55646 drflac_int8 lpcShift;
55647 drflac_int32 coefficients[32];
55648 for (i = 0; i < lpcOrder; ++i) {
55649 drflac_int32 sample;
55650 if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
55651 return DRFLAC_FALSE;
55653 pDecodedSamples[i] = sample;
55655 if (!drflac__read_uint8(bs, 4, &lpcPrecision)) {
55656 return DRFLAC_FALSE;
55658 if (lpcPrecision == 15) {
55659 return DRFLAC_FALSE;
55662 if (!drflac__read_int8(bs, 5, &lpcShift)) {
55663 return DRFLAC_FALSE;
55665 if (lpcShift < 0) {
55666 return DRFLAC_FALSE;
55668 DRFLAC_ZERO_MEMORY(coefficients, sizeof(coefficients));
55669 for (i = 0; i < lpcOrder; ++i) {
55670 if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) {
55671 return DRFLAC_FALSE;
55674 if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, coefficients, pDecodedSamples)) {
55675 return DRFLAC_FALSE;
55677 return DRFLAC_TRUE;
55679 static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header)
55681 const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000};
55682 const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1};
55683 DRFLAC_ASSERT(bs != NULL);
55684 DRFLAC_ASSERT(header != NULL);
55686 drflac_uint8 crc8 = 0xCE;
55687 drflac_uint8 reserved = 0;
55688 drflac_uint8 blockingStrategy = 0;
55689 drflac_uint8 blockSize = 0;
55690 drflac_uint8 sampleRate = 0;
55691 drflac_uint8 channelAssignment = 0;
55692 drflac_uint8 bitsPerSample = 0;
55693 drflac_bool32 isVariableBlockSize;
55694 if (!drflac__find_and_seek_to_next_sync_code(bs)) {
55695 return DRFLAC_FALSE;
55697 if (!drflac__read_uint8(bs, 1, &reserved)) {
55698 return DRFLAC_FALSE;
55700 if (reserved == 1) {
55703 crc8 = drflac_crc8(crc8, reserved, 1);
55704 if (!drflac__read_uint8(bs, 1, &blockingStrategy)) {
55705 return DRFLAC_FALSE;
55707 crc8 = drflac_crc8(crc8, blockingStrategy, 1);
55708 if (!drflac__read_uint8(bs, 4, &blockSize)) {
55709 return DRFLAC_FALSE;
55711 if (blockSize == 0) {
55714 crc8 = drflac_crc8(crc8, blockSize, 4);
55715 if (!drflac__read_uint8(bs, 4, &sampleRate)) {
55716 return DRFLAC_FALSE;
55718 crc8 = drflac_crc8(crc8, sampleRate, 4);
55719 if (!drflac__read_uint8(bs, 4, &channelAssignment)) {
55720 return DRFLAC_FALSE;
55722 if (channelAssignment > 10) {
55725 crc8 = drflac_crc8(crc8, channelAssignment, 4);
55726 if (!drflac__read_uint8(bs, 3, &bitsPerSample)) {
55727 return DRFLAC_FALSE;
55729 if (bitsPerSample == 3 || bitsPerSample == 7) {
55732 crc8 = drflac_crc8(crc8, bitsPerSample, 3);
55733 if (!drflac__read_uint8(bs, 1, &reserved)) {
55734 return DRFLAC_FALSE;
55736 if (reserved == 1) {
55739 crc8 = drflac_crc8(crc8, reserved, 1);
55740 isVariableBlockSize = blockingStrategy == 1;
55741 if (isVariableBlockSize) {
55742 drflac_uint64 pcmFrameNumber;
55743 drflac_result result = drflac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8);
55744 if (result != DRFLAC_SUCCESS) {
55745 if (result == DRFLAC_AT_END) {
55746 return DRFLAC_FALSE;
55751 header->flacFrameNumber = 0;
55752 header->pcmFrameNumber = pcmFrameNumber;
55754 drflac_uint64 flacFrameNumber = 0;
55755 drflac_result result = drflac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8);
55756 if (result != DRFLAC_SUCCESS) {
55757 if (result == DRFLAC_AT_END) {
55758 return DRFLAC_FALSE;
55763 header->flacFrameNumber = (drflac_uint32)flacFrameNumber;
55764 header->pcmFrameNumber = 0;
55766 DRFLAC_ASSERT(blockSize > 0);
55767 if (blockSize == 1) {
55768 header->blockSizeInPCMFrames = 192;
55769 } else if (blockSize <= 5) {
55770 DRFLAC_ASSERT(blockSize >= 2);
55771 header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2));
55772 } else if (blockSize == 6) {
55773 if (!drflac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) {
55774 return DRFLAC_FALSE;
55776 crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 8);
55777 header->blockSizeInPCMFrames += 1;
55778 } else if (blockSize == 7) {
55779 if (!drflac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) {
55780 return DRFLAC_FALSE;
55782 crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 16);
55783 header->blockSizeInPCMFrames += 1;
55785 DRFLAC_ASSERT(blockSize >= 8);
55786 header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8));
55788 if (sampleRate <= 11) {
55789 header->sampleRate = sampleRateTable[sampleRate];
55790 } else if (sampleRate == 12) {
55791 if (!drflac__read_uint32(bs, 8, &header->sampleRate)) {
55792 return DRFLAC_FALSE;
55794 crc8 = drflac_crc8(crc8, header->sampleRate, 8);
55795 header->sampleRate *= 1000;
55796 } else if (sampleRate == 13) {
55797 if (!drflac__read_uint32(bs, 16, &header->sampleRate)) {
55798 return DRFLAC_FALSE;
55800 crc8 = drflac_crc8(crc8, header->sampleRate, 16);
55801 } else if (sampleRate == 14) {
55802 if (!drflac__read_uint32(bs, 16, &header->sampleRate)) {
55803 return DRFLAC_FALSE;
55805 crc8 = drflac_crc8(crc8, header->sampleRate, 16);
55806 header->sampleRate *= 10;
55810 header->channelAssignment = channelAssignment;
55811 header->bitsPerSample = bitsPerSampleTable[bitsPerSample];
55812 if (header->bitsPerSample == 0) {
55813 header->bitsPerSample = streaminfoBitsPerSample;
55815 if (!drflac__read_uint8(bs, 8, &header->crc8)) {
55816 return DRFLAC_FALSE;
55818 #ifndef DR_FLAC_NO_CRC
55819 if (header->crc8 != crc8) {
55823 return DRFLAC_TRUE;
55826 static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe)
55828 drflac_uint8 header;
55830 if (!drflac__read_uint8(bs, 8, &header)) {
55831 return DRFLAC_FALSE;
55833 if ((header & 0x80) != 0) {
55834 return DRFLAC_FALSE;
55836 type = (header & 0x7E) >> 1;
55838 pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT;
55839 } else if (type == 1) {
55840 pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM;
55842 if ((type & 0x20) != 0) {
55843 pSubframe->subframeType = DRFLAC_SUBFRAME_LPC;
55844 pSubframe->lpcOrder = (drflac_uint8)(type & 0x1F) + 1;
55845 } else if ((type & 0x08) != 0) {
55846 pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED;
55847 pSubframe->lpcOrder = (drflac_uint8)(type & 0x07);
55848 if (pSubframe->lpcOrder > 4) {
55849 pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED;
55850 pSubframe->lpcOrder = 0;
55853 pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED;
55856 if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) {
55857 return DRFLAC_FALSE;
55859 pSubframe->wastedBitsPerSample = 0;
55860 if ((header & 0x01) == 1) {
55861 unsigned int wastedBitsPerSample;
55862 if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) {
55863 return DRFLAC_FALSE;
55865 pSubframe->wastedBitsPerSample = (drflac_uint8)wastedBitsPerSample + 1;
55867 return DRFLAC_TRUE;
55869 static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut)
55871 drflac_subframe* pSubframe;
55872 drflac_uint32 subframeBitsPerSample;
55873 DRFLAC_ASSERT(bs != NULL);
55874 DRFLAC_ASSERT(frame != NULL);
55875 pSubframe = frame->subframes + subframeIndex;
55876 if (!drflac__read_subframe_header(bs, pSubframe)) {
55877 return DRFLAC_FALSE;
55879 subframeBitsPerSample = frame->header.bitsPerSample;
55880 if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
55881 subframeBitsPerSample += 1;
55882 } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
55883 subframeBitsPerSample += 1;
55885 if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {
55886 return DRFLAC_FALSE;
55888 subframeBitsPerSample -= pSubframe->wastedBitsPerSample;
55889 pSubframe->pSamplesS32 = pDecodedSamplesOut;
55890 switch (pSubframe->subframeType)
55892 case DRFLAC_SUBFRAME_CONSTANT:
55894 drflac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
55896 case DRFLAC_SUBFRAME_VERBATIM:
55898 drflac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
55900 case DRFLAC_SUBFRAME_FIXED:
55902 drflac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
55904 case DRFLAC_SUBFRAME_LPC:
55906 drflac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
55908 default: return DRFLAC_FALSE;
55910 return DRFLAC_TRUE;
55912 static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex)
55914 drflac_subframe* pSubframe;
55915 drflac_uint32 subframeBitsPerSample;
55916 DRFLAC_ASSERT(bs != NULL);
55917 DRFLAC_ASSERT(frame != NULL);
55918 pSubframe = frame->subframes + subframeIndex;
55919 if (!drflac__read_subframe_header(bs, pSubframe)) {
55920 return DRFLAC_FALSE;
55922 subframeBitsPerSample = frame->header.bitsPerSample;
55923 if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
55924 subframeBitsPerSample += 1;
55925 } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
55926 subframeBitsPerSample += 1;
55928 if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {
55929 return DRFLAC_FALSE;
55931 subframeBitsPerSample -= pSubframe->wastedBitsPerSample;
55932 pSubframe->pSamplesS32 = NULL;
55933 switch (pSubframe->subframeType)
55935 case DRFLAC_SUBFRAME_CONSTANT:
55937 if (!drflac__seek_bits(bs, subframeBitsPerSample)) {
55938 return DRFLAC_FALSE;
55941 case DRFLAC_SUBFRAME_VERBATIM:
55943 unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample;
55944 if (!drflac__seek_bits(bs, bitsToSeek)) {
55945 return DRFLAC_FALSE;
55948 case DRFLAC_SUBFRAME_FIXED:
55950 unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;
55951 if (!drflac__seek_bits(bs, bitsToSeek)) {
55952 return DRFLAC_FALSE;
55954 if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) {
55955 return DRFLAC_FALSE;
55958 case DRFLAC_SUBFRAME_LPC:
55960 drflac_uint8 lpcPrecision;
55961 unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;
55962 if (!drflac__seek_bits(bs, bitsToSeek)) {
55963 return DRFLAC_FALSE;
55965 if (!drflac__read_uint8(bs, 4, &lpcPrecision)) {
55966 return DRFLAC_FALSE;
55968 if (lpcPrecision == 15) {
55969 return DRFLAC_FALSE;
55972 bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5;
55973 if (!drflac__seek_bits(bs, bitsToSeek)) {
55974 return DRFLAC_FALSE;
55976 if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) {
55977 return DRFLAC_FALSE;
55980 default: return DRFLAC_FALSE;
55982 return DRFLAC_TRUE;
55984 static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment)
55986 drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2};
55987 DRFLAC_ASSERT(channelAssignment <= 10);
55988 return lookup[channelAssignment];
55990 static drflac_result drflac__decode_flac_frame(drflac* pFlac)
55994 drflac_uint8 paddingSizeInBits;
55995 drflac_uint16 desiredCRC16;
55996 #ifndef DR_FLAC_NO_CRC
55997 drflac_uint16 actualCRC16;
55999 DRFLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes));
56000 if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) {
56001 return DRFLAC_ERROR;
56003 channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
56004 if (channelCount != (int)pFlac->channels) {
56005 return DRFLAC_ERROR;
56007 for (i = 0; i < channelCount; ++i) {
56008 if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) {
56009 return DRFLAC_ERROR;
56012 paddingSizeInBits = (drflac_uint8)(DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7);
56013 if (paddingSizeInBits > 0) {
56014 drflac_uint8 padding = 0;
56015 if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) {
56016 return DRFLAC_AT_END;
56019 #ifndef DR_FLAC_NO_CRC
56020 actualCRC16 = drflac__flush_crc16(&pFlac->bs);
56022 if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
56023 return DRFLAC_AT_END;
56025 #ifndef DR_FLAC_NO_CRC
56026 if (actualCRC16 != desiredCRC16) {
56027 return DRFLAC_CRC_MISMATCH;
56030 pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
56031 return DRFLAC_SUCCESS;
56033 static drflac_result drflac__seek_flac_frame(drflac* pFlac)
56037 drflac_uint16 desiredCRC16;
56038 #ifndef DR_FLAC_NO_CRC
56039 drflac_uint16 actualCRC16;
56041 channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
56042 for (i = 0; i < channelCount; ++i) {
56043 if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) {
56044 return DRFLAC_ERROR;
56047 if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) {
56048 return DRFLAC_ERROR;
56050 #ifndef DR_FLAC_NO_CRC
56051 actualCRC16 = drflac__flush_crc16(&pFlac->bs);
56053 if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
56054 return DRFLAC_AT_END;
56056 #ifndef DR_FLAC_NO_CRC
56057 if (actualCRC16 != desiredCRC16) {
56058 return DRFLAC_CRC_MISMATCH;
56061 return DRFLAC_SUCCESS;
56063 static drflac_bool32 drflac__read_and_decode_next_flac_frame(drflac* pFlac)
56065 DRFLAC_ASSERT(pFlac != NULL);
56067 drflac_result result;
56068 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
56069 return DRFLAC_FALSE;
56071 result = drflac__decode_flac_frame(pFlac);
56072 if (result != DRFLAC_SUCCESS) {
56073 if (result == DRFLAC_CRC_MISMATCH) {
56076 return DRFLAC_FALSE;
56079 return DRFLAC_TRUE;
56082 static void drflac__get_pcm_frame_range_of_current_flac_frame(drflac* pFlac, drflac_uint64* pFirstPCMFrame, drflac_uint64* pLastPCMFrame)
56084 drflac_uint64 firstPCMFrame;
56085 drflac_uint64 lastPCMFrame;
56086 DRFLAC_ASSERT(pFlac != NULL);
56087 firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber;
56088 if (firstPCMFrame == 0) {
56089 firstPCMFrame = ((drflac_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames;
56091 lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
56092 if (lastPCMFrame > 0) {
56095 if (pFirstPCMFrame) {
56096 *pFirstPCMFrame = firstPCMFrame;
56098 if (pLastPCMFrame) {
56099 *pLastPCMFrame = lastPCMFrame;
56102 static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac)
56104 drflac_bool32 result;
56105 DRFLAC_ASSERT(pFlac != NULL);
56106 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes);
56107 DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));
56108 pFlac->currentPCMFrame = 0;
56111 static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_frame(drflac* pFlac)
56113 DRFLAC_ASSERT(pFlac != NULL);
56114 return drflac__seek_flac_frame(pFlac);
56116 static drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek)
56118 drflac_uint64 pcmFramesRead = 0;
56119 while (pcmFramesToSeek > 0) {
56120 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
56121 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
56125 if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) {
56126 pcmFramesRead += pcmFramesToSeek;
56127 pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)pcmFramesToSeek;
56128 pcmFramesToSeek = 0;
56130 pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining;
56131 pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining;
56132 pFlac->currentFLACFrame.pcmFramesRemaining = 0;
56136 pFlac->currentPCMFrame += pcmFramesRead;
56137 return pcmFramesRead;
56139 static drflac_bool32 drflac__seek_to_pcm_frame__brute_force(drflac* pFlac, drflac_uint64 pcmFrameIndex)
56141 drflac_bool32 isMidFrame = DRFLAC_FALSE;
56142 drflac_uint64 runningPCMFrameCount;
56143 DRFLAC_ASSERT(pFlac != NULL);
56144 if (pcmFrameIndex >= pFlac->currentPCMFrame) {
56145 runningPCMFrameCount = pFlac->currentPCMFrame;
56146 if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
56147 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
56148 return DRFLAC_FALSE;
56151 isMidFrame = DRFLAC_TRUE;
56154 runningPCMFrameCount = 0;
56155 if (!drflac__seek_to_first_frame(pFlac)) {
56156 return DRFLAC_FALSE;
56158 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
56159 return DRFLAC_FALSE;
56163 drflac_uint64 pcmFrameCountInThisFLACFrame;
56164 drflac_uint64 firstPCMFrameInFLACFrame = 0;
56165 drflac_uint64 lastPCMFrameInFLACFrame = 0;
56166 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
56167 pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
56168 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {
56169 drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;
56171 drflac_result result = drflac__decode_flac_frame(pFlac);
56172 if (result == DRFLAC_SUCCESS) {
56173 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
56175 if (result == DRFLAC_CRC_MISMATCH) {
56176 goto next_iteration;
56178 return DRFLAC_FALSE;
56182 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
56186 drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
56187 if (result == DRFLAC_SUCCESS) {
56188 runningPCMFrameCount += pcmFrameCountInThisFLACFrame;
56190 if (result == DRFLAC_CRC_MISMATCH) {
56191 goto next_iteration;
56193 return DRFLAC_FALSE;
56197 runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;
56198 pFlac->currentFLACFrame.pcmFramesRemaining = 0;
56199 isMidFrame = DRFLAC_FALSE;
56201 if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {
56202 return DRFLAC_TRUE;
56206 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
56207 return DRFLAC_FALSE;
56211 #if !defined(DR_FLAC_NO_CRC)
56212 #define DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f
56213 static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFlac, drflac_uint64 targetByte, drflac_uint64 rangeLo, drflac_uint64 rangeHi, drflac_uint64* pLastSuccessfulSeekOffset)
56215 DRFLAC_ASSERT(pFlac != NULL);
56216 DRFLAC_ASSERT(pLastSuccessfulSeekOffset != NULL);
56217 DRFLAC_ASSERT(targetByte >= rangeLo);
56218 DRFLAC_ASSERT(targetByte <= rangeHi);
56219 *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes;
56221 drflac_uint64 lastTargetByte = targetByte;
56222 if (!drflac__seek_to_byte(&pFlac->bs, targetByte)) {
56223 if (targetByte == 0) {
56224 drflac__seek_to_first_frame(pFlac);
56225 return DRFLAC_FALSE;
56227 targetByte = rangeLo + ((rangeHi - rangeLo)/2);
56228 rangeHi = targetByte;
56230 DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));
56232 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
56233 targetByte = rangeLo + ((rangeHi - rangeLo)/2);
56234 rangeHi = targetByte;
56239 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
56240 targetByte = rangeLo + ((rangeHi - rangeLo)/2);
56241 rangeHi = targetByte;
56247 if(targetByte == lastTargetByte) {
56248 return DRFLAC_FALSE;
56251 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);
56252 DRFLAC_ASSERT(targetByte <= rangeHi);
56253 *pLastSuccessfulSeekOffset = targetByte;
56254 return DRFLAC_TRUE;
56256 static drflac_bool32 drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 offset)
56259 if (drflac__decode_flac_frame(pFlac) != DRFLAC_SUCCESS) {
56260 if (drflac__read_and_decode_next_flac_frame(pFlac) == DRFLAC_FALSE) {
56261 return DRFLAC_FALSE;
56265 return drflac__seek_forward_by_pcm_frames(pFlac, offset) == offset;
56267 static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* pFlac, drflac_uint64 pcmFrameIndex, drflac_uint64 byteRangeLo, drflac_uint64 byteRangeHi)
56269 drflac_uint64 targetByte;
56270 drflac_uint64 pcmRangeLo = pFlac->totalPCMFrameCount;
56271 drflac_uint64 pcmRangeHi = 0;
56272 drflac_uint64 lastSuccessfulSeekOffset = (drflac_uint64)-1;
56273 drflac_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo;
56274 drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;
56275 targetByte = byteRangeLo + (drflac_uint64)(((drflac_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO);
56276 if (targetByte > byteRangeHi) {
56277 targetByte = byteRangeHi;
56280 if (drflac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) {
56281 drflac_uint64 newPCMRangeLo;
56282 drflac_uint64 newPCMRangeHi;
56283 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi);
56284 if (pcmRangeLo == newPCMRangeLo) {
56285 if (!drflac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) {
56288 if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {
56289 return DRFLAC_TRUE;
56294 pcmRangeLo = newPCMRangeLo;
56295 pcmRangeHi = newPCMRangeHi;
56296 if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) {
56297 if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) {
56298 return DRFLAC_TRUE;
56303 const float approxCompressionRatio = (drflac_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((drflac_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f);
56304 if (pcmRangeLo > pcmFrameIndex) {
56305 byteRangeHi = lastSuccessfulSeekOffset;
56306 if (byteRangeLo > byteRangeHi) {
56307 byteRangeLo = byteRangeHi;
56309 targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2);
56310 if (targetByte < byteRangeLo) {
56311 targetByte = byteRangeLo;
56314 if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) {
56315 if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {
56316 return DRFLAC_TRUE;
56321 byteRangeLo = lastSuccessfulSeekOffset;
56322 if (byteRangeHi < byteRangeLo) {
56323 byteRangeHi = byteRangeLo;
56325 targetByte = lastSuccessfulSeekOffset + (drflac_uint64)(((drflac_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio);
56326 if (targetByte > byteRangeHi) {
56327 targetByte = byteRangeHi;
56329 if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) {
56330 closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset;
56339 drflac__seek_to_first_frame(pFlac);
56340 return DRFLAC_FALSE;
56342 static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drflac_uint64 pcmFrameIndex)
56344 drflac_uint64 byteRangeLo;
56345 drflac_uint64 byteRangeHi;
56346 drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;
56347 if (drflac__seek_to_first_frame(pFlac) == DRFLAC_FALSE) {
56348 return DRFLAC_FALSE;
56350 if (pcmFrameIndex < seekForwardThreshold) {
56351 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex;
56353 byteRangeLo = pFlac->firstFLACFramePosInBytes;
56354 byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f);
56355 return drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi);
56358 static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac_uint64 pcmFrameIndex)
56360 drflac_uint32 iClosestSeekpoint = 0;
56361 drflac_bool32 isMidFrame = DRFLAC_FALSE;
56362 drflac_uint64 runningPCMFrameCount;
56363 drflac_uint32 iSeekpoint;
56364 DRFLAC_ASSERT(pFlac != NULL);
56365 if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) {
56366 return DRFLAC_FALSE;
56368 for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
56369 if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) {
56372 iClosestSeekpoint = iSeekpoint;
56374 if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) {
56375 return DRFLAC_FALSE;
56377 if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) {
56378 return DRFLAC_FALSE;
56380 #if !defined(DR_FLAC_NO_CRC)
56381 if (pFlac->totalPCMFrameCount > 0) {
56382 drflac_uint64 byteRangeLo;
56383 drflac_uint64 byteRangeHi;
56384 byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f);
56385 byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset;
56386 if (iClosestSeekpoint < pFlac->seekpointCount-1) {
56387 drflac_uint32 iNextSeekpoint = iClosestSeekpoint + 1;
56388 if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) {
56389 return DRFLAC_FALSE;
56391 if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((drflac_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) {
56392 byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1;
56395 if (drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {
56396 if (drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
56397 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);
56398 if (drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) {
56399 return DRFLAC_TRUE;
56405 if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) {
56406 runningPCMFrameCount = pFlac->currentPCMFrame;
56407 if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
56408 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
56409 return DRFLAC_FALSE;
56412 isMidFrame = DRFLAC_TRUE;
56415 runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame;
56416 if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {
56417 return DRFLAC_FALSE;
56419 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
56420 return DRFLAC_FALSE;
56424 drflac_uint64 pcmFrameCountInThisFLACFrame;
56425 drflac_uint64 firstPCMFrameInFLACFrame = 0;
56426 drflac_uint64 lastPCMFrameInFLACFrame = 0;
56427 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
56428 pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
56429 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {
56430 drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;
56432 drflac_result result = drflac__decode_flac_frame(pFlac);
56433 if (result == DRFLAC_SUCCESS) {
56434 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
56436 if (result == DRFLAC_CRC_MISMATCH) {
56437 goto next_iteration;
56439 return DRFLAC_FALSE;
56443 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
56447 drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
56448 if (result == DRFLAC_SUCCESS) {
56449 runningPCMFrameCount += pcmFrameCountInThisFLACFrame;
56451 if (result == DRFLAC_CRC_MISMATCH) {
56452 goto next_iteration;
56454 return DRFLAC_FALSE;
56458 runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;
56459 pFlac->currentFLACFrame.pcmFramesRemaining = 0;
56460 isMidFrame = DRFLAC_FALSE;
56462 if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {
56463 return DRFLAC_TRUE;
56467 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
56468 return DRFLAC_FALSE;
56472 #ifndef DR_FLAC_NO_OGG
56475 drflac_uint8 capturePattern[4];
56476 drflac_uint8 structureVersion;
56477 drflac_uint8 headerType;
56478 drflac_uint64 granulePosition;
56479 drflac_uint32 serialNumber;
56480 drflac_uint32 sequenceNumber;
56481 drflac_uint32 checksum;
56482 drflac_uint8 segmentCount;
56483 drflac_uint8 segmentTable[255];
56484 } drflac_ogg_page_header;
56488 drflac_read_proc onRead;
56489 drflac_seek_proc onSeek;
56490 drflac_meta_proc onMeta;
56491 drflac_container container;
56494 drflac_uint32 sampleRate;
56495 drflac_uint8 channels;
56496 drflac_uint8 bitsPerSample;
56497 drflac_uint64 totalPCMFrameCount;
56498 drflac_uint16 maxBlockSizeInPCMFrames;
56499 drflac_uint64 runningFilePos;
56500 drflac_bool32 hasStreamInfoBlock;
56501 drflac_bool32 hasMetadataBlocks;
56503 drflac_frame_header firstFrameHeader;
56504 #ifndef DR_FLAC_NO_OGG
56505 drflac_uint32 oggSerial;
56506 drflac_uint64 oggFirstBytePos;
56507 drflac_ogg_page_header oggBosHeader;
56509 } drflac_init_info;
56510 static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize)
56512 blockHeader = drflac__be2host_32(blockHeader);
56513 *isLastBlock = (drflac_uint8)((blockHeader & 0x80000000UL) >> 31);
56514 *blockType = (drflac_uint8)((blockHeader & 0x7F000000UL) >> 24);
56515 *blockSize = (blockHeader & 0x00FFFFFFUL);
56517 static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize)
56519 drflac_uint32 blockHeader;
56521 if (onRead(pUserData, &blockHeader, 4) != 4) {
56522 return DRFLAC_FALSE;
56524 drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize);
56525 return DRFLAC_TRUE;
56527 static drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo)
56529 drflac_uint32 blockSizes;
56530 drflac_uint64 frameSizes = 0;
56531 drflac_uint64 importantProps;
56532 drflac_uint8 md5[16];
56533 if (onRead(pUserData, &blockSizes, 4) != 4) {
56534 return DRFLAC_FALSE;
56536 if (onRead(pUserData, &frameSizes, 6) != 6) {
56537 return DRFLAC_FALSE;
56539 if (onRead(pUserData, &importantProps, 8) != 8) {
56540 return DRFLAC_FALSE;
56542 if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) {
56543 return DRFLAC_FALSE;
56545 blockSizes = drflac__be2host_32(blockSizes);
56546 frameSizes = drflac__be2host_64(frameSizes);
56547 importantProps = drflac__be2host_64(importantProps);
56548 pStreamInfo->minBlockSizeInPCMFrames = (drflac_uint16)((blockSizes & 0xFFFF0000) >> 16);
56549 pStreamInfo->maxBlockSizeInPCMFrames = (drflac_uint16) (blockSizes & 0x0000FFFF);
56550 pStreamInfo->minFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 24)) >> 40);
56551 pStreamInfo->maxFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 0)) >> 16);
56552 pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (((drflac_uint64)0x000FFFFF << 16) << 28)) >> 44);
56553 pStreamInfo->channels = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000000E << 16) << 24)) >> 41) + 1;
56554 pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000001F << 16) << 20)) >> 36) + 1;
56555 pStreamInfo->totalPCMFrameCount = ((importantProps & ((((drflac_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF)));
56556 DRFLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5));
56557 return DRFLAC_TRUE;
56559 static void* drflac__malloc_default(size_t sz, void* pUserData)
56562 return DRFLAC_MALLOC(sz);
56564 static void* drflac__realloc_default(void* p, size_t sz, void* pUserData)
56567 return DRFLAC_REALLOC(p, sz);
56569 static void drflac__free_default(void* p, void* pUserData)
56574 static void* drflac__malloc_from_callbacks(size_t sz, const drflac_allocation_callbacks* pAllocationCallbacks)
56576 if (pAllocationCallbacks == NULL) {
56579 if (pAllocationCallbacks->onMalloc != NULL) {
56580 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
56582 if (pAllocationCallbacks->onRealloc != NULL) {
56583 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
56587 static void* drflac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drflac_allocation_callbacks* pAllocationCallbacks)
56589 if (pAllocationCallbacks == NULL) {
56592 if (pAllocationCallbacks->onRealloc != NULL) {
56593 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
56595 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
56597 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
56602 DRFLAC_COPY_MEMORY(p2, p, szOld);
56603 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
56609 static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbacks* pAllocationCallbacks)
56611 if (p == NULL || pAllocationCallbacks == NULL) {
56614 if (pAllocationCallbacks->onFree != NULL) {
56615 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
56618 static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeektableSize, drflac_allocation_callbacks* pAllocationCallbacks)
56620 drflac_uint64 runningFilePos = 42;
56621 drflac_uint64 seektablePos = 0;
56622 drflac_uint32 seektableSize = 0;
56624 drflac_metadata metadata;
56625 drflac_uint8 isLastBlock = 0;
56626 drflac_uint8 blockType;
56627 drflac_uint32 blockSize;
56628 if (drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == DRFLAC_FALSE) {
56629 return DRFLAC_FALSE;
56631 runningFilePos += 4;
56632 metadata.type = blockType;
56633 metadata.pRawData = NULL;
56634 metadata.rawDataSize = 0;
56637 case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION:
56639 if (blockSize < 4) {
56640 return DRFLAC_FALSE;
56643 void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
56644 if (pRawData == NULL) {
56645 return DRFLAC_FALSE;
56647 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
56648 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56649 return DRFLAC_FALSE;
56651 metadata.pRawData = pRawData;
56652 metadata.rawDataSize = blockSize;
56653 metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData);
56654 metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32));
56655 metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32);
56656 onMeta(pUserDataMD, &metadata);
56657 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56660 case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE:
56662 seektablePos = runningFilePos;
56663 seektableSize = blockSize;
56665 drflac_uint32 iSeekpoint;
56667 pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
56668 if (pRawData == NULL) {
56669 return DRFLAC_FALSE;
56671 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
56672 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56673 return DRFLAC_FALSE;
56675 metadata.pRawData = pRawData;
56676 metadata.rawDataSize = blockSize;
56677 metadata.data.seektable.seekpointCount = blockSize/sizeof(drflac_seekpoint);
56678 metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData;
56679 for (iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) {
56680 drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint;
56681 pSeekpoint->firstPCMFrame = drflac__be2host_64(pSeekpoint->firstPCMFrame);
56682 pSeekpoint->flacFrameOffset = drflac__be2host_64(pSeekpoint->flacFrameOffset);
56683 pSeekpoint->pcmFrameCount = drflac__be2host_16(pSeekpoint->pcmFrameCount);
56685 onMeta(pUserDataMD, &metadata);
56686 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56689 case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT:
56691 if (blockSize < 8) {
56692 return DRFLAC_FALSE;
56696 const char* pRunningData;
56697 const char* pRunningDataEnd;
56699 pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
56700 if (pRawData == NULL) {
56701 return DRFLAC_FALSE;
56703 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
56704 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56705 return DRFLAC_FALSE;
56707 metadata.pRawData = pRawData;
56708 metadata.rawDataSize = blockSize;
56709 pRunningData = (const char*)pRawData;
56710 pRunningDataEnd = (const char*)pRawData + blockSize;
56711 metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
56712 if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) {
56713 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56714 return DRFLAC_FALSE;
56716 metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength;
56717 metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
56718 if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) {
56719 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56720 return DRFLAC_FALSE;
56722 metadata.data.vorbis_comment.pComments = pRunningData;
56723 for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) {
56724 drflac_uint32 commentLength;
56725 if (pRunningDataEnd - pRunningData < 4) {
56726 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56727 return DRFLAC_FALSE;
56729 commentLength = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
56730 if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) {
56731 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56732 return DRFLAC_FALSE;
56734 pRunningData += commentLength;
56736 onMeta(pUserDataMD, &metadata);
56737 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56740 case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET:
56742 if (blockSize < 396) {
56743 return DRFLAC_FALSE;
56747 const char* pRunningData;
56748 const char* pRunningDataEnd;
56749 drflac_uint8 iTrack;
56750 drflac_uint8 iIndex;
56751 pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
56752 if (pRawData == NULL) {
56753 return DRFLAC_FALSE;
56755 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
56756 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56757 return DRFLAC_FALSE;
56759 metadata.pRawData = pRawData;
56760 metadata.rawDataSize = blockSize;
56761 pRunningData = (const char*)pRawData;
56762 pRunningDataEnd = (const char*)pRawData + blockSize;
56763 DRFLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128;
56764 metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8;
56765 metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259;
56766 metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1;
56767 metadata.data.cuesheet.pTrackData = pRunningData;
56768 for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) {
56769 drflac_uint8 indexCount;
56770 drflac_uint32 indexPointSize;
56771 if (pRunningDataEnd - pRunningData < 36) {
56772 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56773 return DRFLAC_FALSE;
56775 pRunningData += 35;
56776 indexCount = pRunningData[0]; pRunningData += 1;
56777 indexPointSize = indexCount * sizeof(drflac_cuesheet_track_index);
56778 if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) {
56779 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56780 return DRFLAC_FALSE;
56782 for (iIndex = 0; iIndex < indexCount; ++iIndex) {
56783 drflac_cuesheet_track_index* pTrack = (drflac_cuesheet_track_index*)pRunningData;
56784 pRunningData += sizeof(drflac_cuesheet_track_index);
56785 pTrack->offset = drflac__be2host_64(pTrack->offset);
56788 onMeta(pUserDataMD, &metadata);
56789 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56792 case DRFLAC_METADATA_BLOCK_TYPE_PICTURE:
56794 if (blockSize < 32) {
56795 return DRFLAC_FALSE;
56799 const char* pRunningData;
56800 const char* pRunningDataEnd;
56801 pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
56802 if (pRawData == NULL) {
56803 return DRFLAC_FALSE;
56805 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
56806 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56807 return DRFLAC_FALSE;
56809 metadata.pRawData = pRawData;
56810 metadata.rawDataSize = blockSize;
56811 pRunningData = (const char*)pRawData;
56812 pRunningDataEnd = (const char*)pRawData + blockSize;
56813 metadata.data.picture.type = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
56814 metadata.data.picture.mimeLength = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
56815 if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) {
56816 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56817 return DRFLAC_FALSE;
56819 metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength;
56820 metadata.data.picture.descriptionLength = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
56821 if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) {
56822 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56823 return DRFLAC_FALSE;
56825 metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength;
56826 metadata.data.picture.width = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
56827 metadata.data.picture.height = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
56828 metadata.data.picture.colorDepth = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
56829 metadata.data.picture.indexColorCount = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
56830 metadata.data.picture.pictureDataSize = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
56831 metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData;
56832 if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) {
56833 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56834 return DRFLAC_FALSE;
56836 onMeta(pUserDataMD, &metadata);
56837 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56840 case DRFLAC_METADATA_BLOCK_TYPE_PADDING:
56843 metadata.data.padding.unused = 0;
56844 if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
56845 isLastBlock = DRFLAC_TRUE;
56847 onMeta(pUserDataMD, &metadata);
56851 case DRFLAC_METADATA_BLOCK_TYPE_INVALID:
56854 if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
56855 isLastBlock = DRFLAC_TRUE;
56862 void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
56863 if (pRawData == NULL) {
56864 return DRFLAC_FALSE;
56866 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
56867 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56868 return DRFLAC_FALSE;
56870 metadata.pRawData = pRawData;
56871 metadata.rawDataSize = blockSize;
56872 onMeta(pUserDataMD, &metadata);
56873 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
56877 if (onMeta == NULL && blockSize > 0) {
56878 if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
56879 isLastBlock = DRFLAC_TRUE;
56882 runningFilePos += blockSize;
56887 *pSeektablePos = seektablePos;
56888 *pSeektableSize = seektableSize;
56889 *pFirstFramePos = runningFilePos;
56890 return DRFLAC_TRUE;
56892 static drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed)
56894 drflac_uint8 isLastBlock;
56895 drflac_uint8 blockType;
56896 drflac_uint32 blockSize;
56898 pInit->container = drflac_container_native;
56899 if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
56900 return DRFLAC_FALSE;
56902 if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
56904 return DRFLAC_FALSE;
56906 pInit->hasStreamInfoBlock = DRFLAC_FALSE;
56907 pInit->hasMetadataBlocks = DRFLAC_FALSE;
56908 if (!drflac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) {
56909 return DRFLAC_FALSE;
56911 if (pInit->firstFrameHeader.bitsPerSample == 0) {
56912 return DRFLAC_FALSE;
56914 pInit->sampleRate = pInit->firstFrameHeader.sampleRate;
56915 pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment);
56916 pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample;
56917 pInit->maxBlockSizeInPCMFrames = 65535;
56918 return DRFLAC_TRUE;
56921 drflac_streaminfo streaminfo;
56922 if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) {
56923 return DRFLAC_FALSE;
56925 pInit->hasStreamInfoBlock = DRFLAC_TRUE;
56926 pInit->sampleRate = streaminfo.sampleRate;
56927 pInit->channels = streaminfo.channels;
56928 pInit->bitsPerSample = streaminfo.bitsPerSample;
56929 pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount;
56930 pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames;
56931 pInit->hasMetadataBlocks = !isLastBlock;
56933 drflac_metadata metadata;
56934 metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO;
56935 metadata.pRawData = NULL;
56936 metadata.rawDataSize = 0;
56937 metadata.data.streaminfo = streaminfo;
56938 onMeta(pUserDataMD, &metadata);
56940 return DRFLAC_TRUE;
56943 #ifndef DR_FLAC_NO_OGG
56944 #define DRFLAC_OGG_MAX_PAGE_SIZE 65307
56945 #define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199
56948 drflac_ogg_recover_on_crc_mismatch,
56949 drflac_ogg_fail_on_crc_mismatch
56950 } drflac_ogg_crc_mismatch_recovery;
56951 #ifndef DR_FLAC_NO_CRC
56952 static drflac_uint32 drflac__crc32_table[] = {
56953 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L,
56954 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L,
56955 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L,
56956 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL,
56957 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L,
56958 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L,
56959 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L,
56960 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL,
56961 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L,
56962 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L,
56963 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L,
56964 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL,
56965 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L,
56966 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L,
56967 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L,
56968 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL,
56969 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL,
56970 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L,
56971 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L,
56972 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL,
56973 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL,
56974 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L,
56975 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L,
56976 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL,
56977 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL,
56978 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L,
56979 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L,
56980 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL,
56981 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL,
56982 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L,
56983 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L,
56984 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL,
56985 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L,
56986 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL,
56987 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL,
56988 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L,
56989 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L,
56990 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL,
56991 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL,
56992 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L,
56993 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L,
56994 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL,
56995 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL,
56996 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L,
56997 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L,
56998 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL,
56999 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL,
57000 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L,
57001 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L,
57002 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL,
57003 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L,
57004 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L,
57005 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L,
57006 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL,
57007 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L,
57008 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L,
57009 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L,
57010 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL,
57011 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L,
57012 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L,
57013 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L,
57014 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL,
57015 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L,
57016 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L
57019 static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data)
57021 #ifndef DR_FLAC_NO_CRC
57022 return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data];
57029 static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data)
57031 crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF));
57032 crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF));
57033 crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF));
57034 crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF));
57037 static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data)
57039 crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF));
57040 crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF));
57044 static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize)
57047 for (i = 0; i < dataSize; ++i) {
57048 crc32 = drflac_crc32_byte(crc32, pData[i]);
57052 static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4])
57054 return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S';
57056 static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader)
57058 return 27 + pHeader->segmentCount;
57060 static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader)
57062 drflac_uint32 pageBodySize = 0;
57064 for (i = 0; i < pHeader->segmentCount; ++i) {
57065 pageBodySize += pHeader->segmentTable[i];
57067 return pageBodySize;
57069 static drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32)
57071 drflac_uint8 data[23];
57073 DRFLAC_ASSERT(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32);
57074 if (onRead(pUserData, data, 23) != 23) {
57075 return DRFLAC_AT_END;
57078 pHeader->capturePattern[0] = 'O';
57079 pHeader->capturePattern[1] = 'g';
57080 pHeader->capturePattern[2] = 'g';
57081 pHeader->capturePattern[3] = 'S';
57082 pHeader->structureVersion = data[0];
57083 pHeader->headerType = data[1];
57084 DRFLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8);
57085 DRFLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4);
57086 DRFLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4);
57087 DRFLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4);
57088 pHeader->segmentCount = data[22];
57093 for (i = 0; i < 23; ++i) {
57094 *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]);
57096 if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) {
57097 return DRFLAC_AT_END;
57099 *pBytesRead += pHeader->segmentCount;
57100 for (i = 0; i < pHeader->segmentCount; ++i) {
57101 *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]);
57103 return DRFLAC_SUCCESS;
57105 static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32)
57107 drflac_uint8 id[4];
57109 if (onRead(pUserData, id, 4) != 4) {
57110 return DRFLAC_AT_END;
57114 if (drflac_ogg__is_capture_pattern(id)) {
57115 drflac_result result;
57116 *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
57117 result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32);
57118 if (result == DRFLAC_SUCCESS) {
57119 return DRFLAC_SUCCESS;
57121 if (result == DRFLAC_CRC_MISMATCH) {
57131 if (onRead(pUserData, &id[3], 1) != 1) {
57132 return DRFLAC_AT_END;
57140 drflac_read_proc onRead;
57141 drflac_seek_proc onSeek;
57143 drflac_uint64 currentBytePos;
57144 drflac_uint64 firstBytePos;
57145 drflac_uint32 serialNumber;
57146 drflac_ogg_page_header bosPageHeader;
57147 drflac_ogg_page_header currentPageHeader;
57148 drflac_uint32 bytesRemainingInPage;
57149 drflac_uint32 pageDataSize;
57150 drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE];
57152 static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead)
57154 size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead);
57155 oggbs->currentBytePos += bytesActuallyRead;
57156 return bytesActuallyRead;
57158 static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin)
57160 if (origin == drflac_seek_origin_start) {
57161 if (offset <= 0x7FFFFFFF) {
57162 if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) {
57163 return DRFLAC_FALSE;
57165 oggbs->currentBytePos = offset;
57166 return DRFLAC_TRUE;
57168 if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
57169 return DRFLAC_FALSE;
57171 oggbs->currentBytePos = offset;
57172 return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current);
57175 while (offset > 0x7FFFFFFF) {
57176 if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
57177 return DRFLAC_FALSE;
57179 oggbs->currentBytePos += 0x7FFFFFFF;
57180 offset -= 0x7FFFFFFF;
57182 if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) {
57183 return DRFLAC_FALSE;
57185 oggbs->currentBytePos += offset;
57186 return DRFLAC_TRUE;
57189 static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod)
57191 drflac_ogg_page_header header;
57193 drflac_uint32 crc32 = 0;
57194 drflac_uint32 bytesRead;
57195 drflac_uint32 pageBodySize;
57196 #ifndef DR_FLAC_NO_CRC
57197 drflac_uint32 actualCRC32;
57199 if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
57200 return DRFLAC_FALSE;
57202 oggbs->currentBytePos += bytesRead;
57203 pageBodySize = drflac_ogg__get_page_body_size(&header);
57204 if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) {
57207 if (header.serialNumber != oggbs->serialNumber) {
57208 if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) {
57209 return DRFLAC_FALSE;
57213 if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) {
57214 return DRFLAC_FALSE;
57216 oggbs->pageDataSize = pageBodySize;
57217 #ifndef DR_FLAC_NO_CRC
57218 actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize);
57219 if (actualCRC32 != header.checksum) {
57220 if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) {
57223 drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch);
57224 return DRFLAC_FALSE;
57228 (void)recoveryMethod;
57230 oggbs->currentPageHeader = header;
57231 oggbs->bytesRemainingInPage = pageBodySize;
57232 return DRFLAC_TRUE;
57236 static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg)
57238 drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage;
57239 drflac_uint8 iSeg = 0;
57240 drflac_uint32 iByte = 0;
57241 while (iByte < bytesConsumedInPage) {
57242 drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
57243 if (iByte + segmentSize > bytesConsumedInPage) {
57247 iByte += segmentSize;
57250 *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte);
57253 static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs)
57256 drflac_bool32 atEndOfPage = DRFLAC_FALSE;
57257 drflac_uint8 bytesRemainingInSeg;
57258 drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg);
57259 drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg;
57260 for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) {
57261 drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
57262 if (segmentSize < 255) {
57263 if (iSeg == oggbs->currentPageHeader.segmentCount-1) {
57264 atEndOfPage = DRFLAC_TRUE;
57268 bytesToEndOfPacketOrPage += segmentSize;
57270 drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current);
57271 oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage;
57273 if (!drflac_oggbs__goto_next_page(oggbs)) {
57274 return DRFLAC_FALSE;
57276 if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
57277 return DRFLAC_TRUE;
57280 return DRFLAC_TRUE;
57284 static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs)
57286 return drflac_oggbs__seek_to_next_packet(oggbs);
57289 static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead)
57291 drflac_oggbs* oggbs = (drflac_oggbs*)pUserData;
57292 drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut;
57293 size_t bytesRead = 0;
57294 DRFLAC_ASSERT(oggbs != NULL);
57295 DRFLAC_ASSERT(pRunningBufferOut != NULL);
57296 while (bytesRead < bytesToRead) {
57297 size_t bytesRemainingToRead = bytesToRead - bytesRead;
57298 if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) {
57299 DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead);
57300 bytesRead += bytesRemainingToRead;
57301 oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead;
57304 if (oggbs->bytesRemainingInPage > 0) {
57305 DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage);
57306 bytesRead += oggbs->bytesRemainingInPage;
57307 pRunningBufferOut += oggbs->bytesRemainingInPage;
57308 oggbs->bytesRemainingInPage = 0;
57310 DRFLAC_ASSERT(bytesRemainingToRead > 0);
57311 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
57317 static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin)
57319 drflac_oggbs* oggbs = (drflac_oggbs*)pUserData;
57320 int bytesSeeked = 0;
57321 DRFLAC_ASSERT(oggbs != NULL);
57322 DRFLAC_ASSERT(offset >= 0);
57323 if (origin == drflac_seek_origin_start) {
57324 if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) {
57325 return DRFLAC_FALSE;
57327 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) {
57328 return DRFLAC_FALSE;
57330 return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current);
57332 DRFLAC_ASSERT(origin == drflac_seek_origin_current);
57333 while (bytesSeeked < offset) {
57334 int bytesRemainingToSeek = offset - bytesSeeked;
57335 DRFLAC_ASSERT(bytesRemainingToSeek >= 0);
57336 if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) {
57337 bytesSeeked += bytesRemainingToSeek;
57339 oggbs->bytesRemainingInPage -= bytesRemainingToSeek;
57342 if (oggbs->bytesRemainingInPage > 0) {
57343 bytesSeeked += (int)oggbs->bytesRemainingInPage;
57344 oggbs->bytesRemainingInPage = 0;
57346 DRFLAC_ASSERT(bytesRemainingToSeek > 0);
57347 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) {
57348 return DRFLAC_FALSE;
57351 return DRFLAC_TRUE;
57353 static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex)
57355 drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
57356 drflac_uint64 originalBytePos;
57357 drflac_uint64 runningGranulePosition;
57358 drflac_uint64 runningFrameBytePos;
57359 drflac_uint64 runningPCMFrameCount;
57360 DRFLAC_ASSERT(oggbs != NULL);
57361 originalBytePos = oggbs->currentBytePos;
57362 if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) {
57363 return DRFLAC_FALSE;
57365 oggbs->bytesRemainingInPage = 0;
57366 runningGranulePosition = 0;
57368 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
57369 drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start);
57370 return DRFLAC_FALSE;
57372 runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize;
57373 if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) {
57376 if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
57377 if (oggbs->currentPageHeader.segmentTable[0] >= 2) {
57378 drflac_uint8 firstBytesInPage[2];
57379 firstBytesInPage[0] = oggbs->pageData[0];
57380 firstBytesInPage[1] = oggbs->pageData[1];
57381 if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) {
57382 runningGranulePosition = oggbs->currentPageHeader.granulePosition;
57388 if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) {
57389 return DRFLAC_FALSE;
57391 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
57392 return DRFLAC_FALSE;
57394 runningPCMFrameCount = runningGranulePosition;
57396 drflac_uint64 firstPCMFrameInFLACFrame = 0;
57397 drflac_uint64 lastPCMFrameInFLACFrame = 0;
57398 drflac_uint64 pcmFrameCountInThisFrame;
57399 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
57400 return DRFLAC_FALSE;
57402 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
57403 pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
57404 if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) {
57405 drflac_result result = drflac__decode_flac_frame(pFlac);
57406 if (result == DRFLAC_SUCCESS) {
57407 pFlac->currentPCMFrame = pcmFrameIndex;
57408 pFlac->currentFLACFrame.pcmFramesRemaining = 0;
57409 return DRFLAC_TRUE;
57411 return DRFLAC_FALSE;
57414 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) {
57415 drflac_result result = drflac__decode_flac_frame(pFlac);
57416 if (result == DRFLAC_SUCCESS) {
57417 drflac_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount);
57418 if (pcmFramesToDecode == 0) {
57419 return DRFLAC_TRUE;
57421 pFlac->currentPCMFrame = runningPCMFrameCount;
57422 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
57424 if (result == DRFLAC_CRC_MISMATCH) {
57427 return DRFLAC_FALSE;
57431 drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
57432 if (result == DRFLAC_SUCCESS) {
57433 runningPCMFrameCount += pcmFrameCountInThisFrame;
57435 if (result == DRFLAC_CRC_MISMATCH) {
57438 return DRFLAC_FALSE;
57444 static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed)
57446 drflac_ogg_page_header header;
57447 drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
57448 drflac_uint32 bytesRead = 0;
57450 pInit->container = drflac_container_ogg;
57451 pInit->oggFirstBytePos = 0;
57452 if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
57453 return DRFLAC_FALSE;
57455 pInit->runningFilePos += bytesRead;
57458 if ((header.headerType & 0x02) == 0) {
57459 return DRFLAC_FALSE;
57461 pageBodySize = drflac_ogg__get_page_body_size(&header);
57462 if (pageBodySize == 51) {
57463 drflac_uint32 bytesRemainingInPage = pageBodySize;
57464 drflac_uint8 packetType;
57465 if (onRead(pUserData, &packetType, 1) != 1) {
57466 return DRFLAC_FALSE;
57468 bytesRemainingInPage -= 1;
57469 if (packetType == 0x7F) {
57470 drflac_uint8 sig[4];
57471 if (onRead(pUserData, sig, 4) != 4) {
57472 return DRFLAC_FALSE;
57474 bytesRemainingInPage -= 4;
57475 if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') {
57476 drflac_uint8 mappingVersion[2];
57477 if (onRead(pUserData, mappingVersion, 2) != 2) {
57478 return DRFLAC_FALSE;
57480 if (mappingVersion[0] != 1) {
57481 return DRFLAC_FALSE;
57483 if (!onSeek(pUserData, 2, drflac_seek_origin_current)) {
57484 return DRFLAC_FALSE;
57486 if (onRead(pUserData, sig, 4) != 4) {
57487 return DRFLAC_FALSE;
57489 if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') {
57490 drflac_streaminfo streaminfo;
57491 drflac_uint8 isLastBlock;
57492 drflac_uint8 blockType;
57493 drflac_uint32 blockSize;
57494 if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
57495 return DRFLAC_FALSE;
57497 if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
57498 return DRFLAC_FALSE;
57500 if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) {
57501 pInit->hasStreamInfoBlock = DRFLAC_TRUE;
57502 pInit->sampleRate = streaminfo.sampleRate;
57503 pInit->channels = streaminfo.channels;
57504 pInit->bitsPerSample = streaminfo.bitsPerSample;
57505 pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount;
57506 pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames;
57507 pInit->hasMetadataBlocks = !isLastBlock;
57509 drflac_metadata metadata;
57510 metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO;
57511 metadata.pRawData = NULL;
57512 metadata.rawDataSize = 0;
57513 metadata.data.streaminfo = streaminfo;
57514 onMeta(pUserDataMD, &metadata);
57516 pInit->runningFilePos += pageBodySize;
57517 pInit->oggFirstBytePos = pInit->runningFilePos - 79;
57518 pInit->oggSerial = header.serialNumber;
57519 pInit->oggBosHeader = header;
57522 return DRFLAC_FALSE;
57525 return DRFLAC_FALSE;
57528 if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
57529 return DRFLAC_FALSE;
57533 if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
57534 return DRFLAC_FALSE;
57538 if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) {
57539 return DRFLAC_FALSE;
57542 pInit->runningFilePos += pageBodySize;
57543 if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
57544 return DRFLAC_FALSE;
57546 pInit->runningFilePos += bytesRead;
57548 pInit->hasMetadataBlocks = DRFLAC_TRUE;
57549 return DRFLAC_TRUE;
57552 static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD)
57554 drflac_bool32 relaxed;
57555 drflac_uint8 id[4];
57556 if (pInit == NULL || onRead == NULL || onSeek == NULL) {
57557 return DRFLAC_FALSE;
57559 DRFLAC_ZERO_MEMORY(pInit, sizeof(*pInit));
57560 pInit->onRead = onRead;
57561 pInit->onSeek = onSeek;
57562 pInit->onMeta = onMeta;
57563 pInit->container = container;
57564 pInit->pUserData = pUserData;
57565 pInit->pUserDataMD = pUserDataMD;
57566 pInit->bs.onRead = onRead;
57567 pInit->bs.onSeek = onSeek;
57568 pInit->bs.pUserData = pUserData;
57569 drflac__reset_cache(&pInit->bs);
57570 relaxed = container != drflac_container_unknown;
57572 if (onRead(pUserData, id, 4) != 4) {
57573 return DRFLAC_FALSE;
57575 pInit->runningFilePos += 4;
57576 if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') {
57577 drflac_uint8 header[6];
57578 drflac_uint8 flags;
57579 drflac_uint32 headerSize;
57580 if (onRead(pUserData, header, 6) != 6) {
57581 return DRFLAC_FALSE;
57583 pInit->runningFilePos += 6;
57585 DRFLAC_COPY_MEMORY(&headerSize, header+2, 4);
57586 headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize));
57587 if (flags & 0x10) {
57590 if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) {
57591 return DRFLAC_FALSE;
57593 pInit->runningFilePos += headerSize;
57598 if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') {
57599 return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
57601 #ifndef DR_FLAC_NO_OGG
57602 if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') {
57603 return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
57607 if (container == drflac_container_native) {
57608 return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
57610 #ifndef DR_FLAC_NO_OGG
57611 if (container == drflac_container_ogg) {
57612 return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
57616 return DRFLAC_FALSE;
57618 static void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit)
57620 DRFLAC_ASSERT(pFlac != NULL);
57621 DRFLAC_ASSERT(pInit != NULL);
57622 DRFLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac));
57623 pFlac->bs = pInit->bs;
57624 pFlac->onMeta = pInit->onMeta;
57625 pFlac->pUserDataMD = pInit->pUserDataMD;
57626 pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames;
57627 pFlac->sampleRate = pInit->sampleRate;
57628 pFlac->channels = (drflac_uint8)pInit->channels;
57629 pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample;
57630 pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount;
57631 pFlac->container = pInit->container;
57633 static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks)
57635 drflac_init_info init;
57636 drflac_uint32 allocationSize;
57637 drflac_uint32 wholeSIMDVectorCountPerChannel;
57638 drflac_uint32 decodedSamplesAllocationSize;
57639 #ifndef DR_FLAC_NO_OGG
57640 drflac_oggbs oggbs;
57642 drflac_uint64 firstFramePos;
57643 drflac_uint64 seektablePos;
57644 drflac_uint32 seektableSize;
57645 drflac_allocation_callbacks allocationCallbacks;
57647 drflac__init_cpu_caps();
57648 if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) {
57651 if (pAllocationCallbacks != NULL) {
57652 allocationCallbacks = *pAllocationCallbacks;
57653 if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) {
57657 allocationCallbacks.pUserData = NULL;
57658 allocationCallbacks.onMalloc = drflac__malloc_default;
57659 allocationCallbacks.onRealloc = drflac__realloc_default;
57660 allocationCallbacks.onFree = drflac__free_default;
57662 allocationSize = sizeof(drflac);
57663 if ((init.maxBlockSizeInPCMFrames % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) {
57664 wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32)));
57666 wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1;
57668 decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels;
57669 allocationSize += decodedSamplesAllocationSize;
57670 allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE;
57671 #ifndef DR_FLAC_NO_OGG
57672 if (init.container == drflac_container_ogg) {
57673 allocationSize += sizeof(drflac_oggbs);
57675 DRFLAC_ZERO_MEMORY(&oggbs, sizeof(oggbs));
57676 if (init.container == drflac_container_ogg) {
57677 oggbs.onRead = onRead;
57678 oggbs.onSeek = onSeek;
57679 oggbs.pUserData = pUserData;
57680 oggbs.currentBytePos = init.oggFirstBytePos;
57681 oggbs.firstBytePos = init.oggFirstBytePos;
57682 oggbs.serialNumber = init.oggSerial;
57683 oggbs.bosPageHeader = init.oggBosHeader;
57684 oggbs.bytesRemainingInPage = 0;
57687 firstFramePos = 42;
57690 if (init.hasMetadataBlocks) {
57691 drflac_read_proc onReadOverride = onRead;
57692 drflac_seek_proc onSeekOverride = onSeek;
57693 void* pUserDataOverride = pUserData;
57694 #ifndef DR_FLAC_NO_OGG
57695 if (init.container == drflac_container_ogg) {
57696 onReadOverride = drflac__on_read_ogg;
57697 onSeekOverride = drflac__on_seek_ogg;
57698 pUserDataOverride = (void*)&oggbs;
57701 if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seektableSize, &allocationCallbacks)) {
57704 allocationSize += seektableSize;
57706 pFlac = (drflac*)drflac__malloc_from_callbacks(allocationSize, &allocationCallbacks);
57707 if (pFlac == NULL) {
57710 drflac__init_from_info(pFlac, &init);
57711 pFlac->allocationCallbacks = allocationCallbacks;
57712 pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE);
57713 #ifndef DR_FLAC_NO_OGG
57714 if (init.container == drflac_container_ogg) {
57715 drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + seektableSize);
57716 *pInternalOggbs = oggbs;
57717 pFlac->bs.onRead = drflac__on_read_ogg;
57718 pFlac->bs.onSeek = drflac__on_seek_ogg;
57719 pFlac->bs.pUserData = (void*)pInternalOggbs;
57720 pFlac->_oggbs = (void*)pInternalOggbs;
57723 pFlac->firstFLACFramePosInBytes = firstFramePos;
57724 #ifndef DR_FLAC_NO_OGG
57725 if (init.container == drflac_container_ogg)
57727 pFlac->pSeekpoints = NULL;
57728 pFlac->seekpointCount = 0;
57733 if (seektablePos != 0) {
57734 pFlac->seekpointCount = seektableSize / sizeof(*pFlac->pSeekpoints);
57735 pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize);
57736 DRFLAC_ASSERT(pFlac->bs.onSeek != NULL);
57737 DRFLAC_ASSERT(pFlac->bs.onRead != NULL);
57738 if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) {
57739 if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints, seektableSize) == seektableSize) {
57740 drflac_uint32 iSeekpoint;
57741 for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
57742 pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame);
57743 pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset);
57744 pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount);
57747 pFlac->pSeekpoints = NULL;
57748 pFlac->seekpointCount = 0;
57750 if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) {
57751 drflac__free_from_callbacks(pFlac, &allocationCallbacks);
57755 pFlac->pSeekpoints = NULL;
57756 pFlac->seekpointCount = 0;
57760 if (!init.hasStreamInfoBlock) {
57761 pFlac->currentFLACFrame.header = init.firstFrameHeader;
57763 drflac_result result = drflac__decode_flac_frame(pFlac);
57764 if (result == DRFLAC_SUCCESS) {
57767 if (result == DRFLAC_CRC_MISMATCH) {
57768 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
57769 drflac__free_from_callbacks(pFlac, &allocationCallbacks);
57774 drflac__free_from_callbacks(pFlac, &allocationCallbacks);
57782 #ifndef DR_FLAC_NO_STDIO
57786 static drflac_result drflac_result_from_errno(int e)
57790 case 0: return DRFLAC_SUCCESS;
57792 case EPERM: return DRFLAC_INVALID_OPERATION;
57795 case ENOENT: return DRFLAC_DOES_NOT_EXIST;
57798 case ESRCH: return DRFLAC_DOES_NOT_EXIST;
57801 case EINTR: return DRFLAC_INTERRUPT;
57804 case EIO: return DRFLAC_IO_ERROR;
57807 case ENXIO: return DRFLAC_DOES_NOT_EXIST;
57810 case E2BIG: return DRFLAC_INVALID_ARGS;
57813 case ENOEXEC: return DRFLAC_INVALID_FILE;
57816 case EBADF: return DRFLAC_INVALID_FILE;
57819 case ECHILD: return DRFLAC_ERROR;
57822 case EAGAIN: return DRFLAC_UNAVAILABLE;
57825 case ENOMEM: return DRFLAC_OUT_OF_MEMORY;
57828 case EACCES: return DRFLAC_ACCESS_DENIED;
57831 case EFAULT: return DRFLAC_BAD_ADDRESS;
57834 case ENOTBLK: return DRFLAC_ERROR;
57837 case EBUSY: return DRFLAC_BUSY;
57840 case EEXIST: return DRFLAC_ALREADY_EXISTS;
57843 case EXDEV: return DRFLAC_ERROR;
57846 case ENODEV: return DRFLAC_DOES_NOT_EXIST;
57849 case ENOTDIR: return DRFLAC_NOT_DIRECTORY;
57852 case EISDIR: return DRFLAC_IS_DIRECTORY;
57855 case EINVAL: return DRFLAC_INVALID_ARGS;
57858 case ENFILE: return DRFLAC_TOO_MANY_OPEN_FILES;
57861 case EMFILE: return DRFLAC_TOO_MANY_OPEN_FILES;
57864 case ENOTTY: return DRFLAC_INVALID_OPERATION;
57867 case ETXTBSY: return DRFLAC_BUSY;
57870 case EFBIG: return DRFLAC_TOO_BIG;
57873 case ENOSPC: return DRFLAC_NO_SPACE;
57876 case ESPIPE: return DRFLAC_BAD_SEEK;
57879 case EROFS: return DRFLAC_ACCESS_DENIED;
57882 case EMLINK: return DRFLAC_TOO_MANY_LINKS;
57885 case EPIPE: return DRFLAC_BAD_PIPE;
57888 case EDOM: return DRFLAC_OUT_OF_RANGE;
57891 case ERANGE: return DRFLAC_OUT_OF_RANGE;
57894 case EDEADLK: return DRFLAC_DEADLOCK;
57896 #ifdef ENAMETOOLONG
57897 case ENAMETOOLONG: return DRFLAC_PATH_TOO_LONG;
57900 case ENOLCK: return DRFLAC_ERROR;
57903 case ENOSYS: return DRFLAC_NOT_IMPLEMENTED;
57906 case ENOTEMPTY: return DRFLAC_DIRECTORY_NOT_EMPTY;
57909 case ELOOP: return DRFLAC_TOO_MANY_LINKS;
57912 case ENOMSG: return DRFLAC_NO_MESSAGE;
57915 case EIDRM: return DRFLAC_ERROR;
57918 case ECHRNG: return DRFLAC_ERROR;
57921 case EL2NSYNC: return DRFLAC_ERROR;
57924 case EL3HLT: return DRFLAC_ERROR;
57927 case EL3RST: return DRFLAC_ERROR;
57930 case ELNRNG: return DRFLAC_OUT_OF_RANGE;
57933 case EUNATCH: return DRFLAC_ERROR;
57936 case ENOCSI: return DRFLAC_ERROR;
57939 case EL2HLT: return DRFLAC_ERROR;
57942 case EBADE: return DRFLAC_ERROR;
57945 case EBADR: return DRFLAC_ERROR;
57948 case EXFULL: return DRFLAC_ERROR;
57951 case ENOANO: return DRFLAC_ERROR;
57954 case EBADRQC: return DRFLAC_ERROR;
57957 case EBADSLT: return DRFLAC_ERROR;
57960 case EBFONT: return DRFLAC_INVALID_FILE;
57963 case ENOSTR: return DRFLAC_ERROR;
57966 case ENODATA: return DRFLAC_NO_DATA_AVAILABLE;
57969 case ETIME: return DRFLAC_TIMEOUT;
57972 case ENOSR: return DRFLAC_NO_DATA_AVAILABLE;
57975 case ENONET: return DRFLAC_NO_NETWORK;
57978 case ENOPKG: return DRFLAC_ERROR;
57981 case EREMOTE: return DRFLAC_ERROR;
57984 case ENOLINK: return DRFLAC_ERROR;
57987 case EADV: return DRFLAC_ERROR;
57990 case ESRMNT: return DRFLAC_ERROR;
57993 case ECOMM: return DRFLAC_ERROR;
57996 case EPROTO: return DRFLAC_ERROR;
57999 case EMULTIHOP: return DRFLAC_ERROR;
58002 case EDOTDOT: return DRFLAC_ERROR;
58005 case EBADMSG: return DRFLAC_BAD_MESSAGE;
58008 case EOVERFLOW: return DRFLAC_TOO_BIG;
58011 case ENOTUNIQ: return DRFLAC_NOT_UNIQUE;
58014 case EBADFD: return DRFLAC_ERROR;
58017 case EREMCHG: return DRFLAC_ERROR;
58020 case ELIBACC: return DRFLAC_ACCESS_DENIED;
58023 case ELIBBAD: return DRFLAC_INVALID_FILE;
58026 case ELIBSCN: return DRFLAC_INVALID_FILE;
58029 case ELIBMAX: return DRFLAC_ERROR;
58032 case ELIBEXEC: return DRFLAC_ERROR;
58035 case EILSEQ: return DRFLAC_INVALID_DATA;
58038 case ERESTART: return DRFLAC_ERROR;
58041 case ESTRPIPE: return DRFLAC_ERROR;
58044 case EUSERS: return DRFLAC_ERROR;
58047 case ENOTSOCK: return DRFLAC_NOT_SOCKET;
58049 #ifdef EDESTADDRREQ
58050 case EDESTADDRREQ: return DRFLAC_NO_ADDRESS;
58053 case EMSGSIZE: return DRFLAC_TOO_BIG;
58056 case EPROTOTYPE: return DRFLAC_BAD_PROTOCOL;
58059 case ENOPROTOOPT: return DRFLAC_PROTOCOL_UNAVAILABLE;
58061 #ifdef EPROTONOSUPPORT
58062 case EPROTONOSUPPORT: return DRFLAC_PROTOCOL_NOT_SUPPORTED;
58064 #ifdef ESOCKTNOSUPPORT
58065 case ESOCKTNOSUPPORT: return DRFLAC_SOCKET_NOT_SUPPORTED;
58068 case EOPNOTSUPP: return DRFLAC_INVALID_OPERATION;
58070 #ifdef EPFNOSUPPORT
58071 case EPFNOSUPPORT: return DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED;
58073 #ifdef EAFNOSUPPORT
58074 case EAFNOSUPPORT: return DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED;
58077 case EADDRINUSE: return DRFLAC_ALREADY_IN_USE;
58079 #ifdef EADDRNOTAVAIL
58080 case EADDRNOTAVAIL: return DRFLAC_ERROR;
58083 case ENETDOWN: return DRFLAC_NO_NETWORK;
58086 case ENETUNREACH: return DRFLAC_NO_NETWORK;
58089 case ENETRESET: return DRFLAC_NO_NETWORK;
58091 #ifdef ECONNABORTED
58092 case ECONNABORTED: return DRFLAC_NO_NETWORK;
58095 case ECONNRESET: return DRFLAC_CONNECTION_RESET;
58098 case ENOBUFS: return DRFLAC_NO_SPACE;
58101 case EISCONN: return DRFLAC_ALREADY_CONNECTED;
58104 case ENOTCONN: return DRFLAC_NOT_CONNECTED;
58107 case ESHUTDOWN: return DRFLAC_ERROR;
58109 #ifdef ETOOMANYREFS
58110 case ETOOMANYREFS: return DRFLAC_ERROR;
58113 case ETIMEDOUT: return DRFLAC_TIMEOUT;
58115 #ifdef ECONNREFUSED
58116 case ECONNREFUSED: return DRFLAC_CONNECTION_REFUSED;
58119 case EHOSTDOWN: return DRFLAC_NO_HOST;
58121 #ifdef EHOSTUNREACH
58122 case EHOSTUNREACH: return DRFLAC_NO_HOST;
58125 case EALREADY: return DRFLAC_IN_PROGRESS;
58128 case EINPROGRESS: return DRFLAC_IN_PROGRESS;
58131 case ESTALE: return DRFLAC_INVALID_FILE;
58134 case EUCLEAN: return DRFLAC_ERROR;
58137 case ENOTNAM: return DRFLAC_ERROR;
58140 case ENAVAIL: return DRFLAC_ERROR;
58143 case EISNAM: return DRFLAC_ERROR;
58146 case EREMOTEIO: return DRFLAC_IO_ERROR;
58149 case EDQUOT: return DRFLAC_NO_SPACE;
58152 case ENOMEDIUM: return DRFLAC_DOES_NOT_EXIST;
58155 case EMEDIUMTYPE: return DRFLAC_ERROR;
58158 case ECANCELED: return DRFLAC_CANCELLED;
58161 case ENOKEY: return DRFLAC_ERROR;
58164 case EKEYEXPIRED: return DRFLAC_ERROR;
58167 case EKEYREVOKED: return DRFLAC_ERROR;
58169 #ifdef EKEYREJECTED
58170 case EKEYREJECTED: return DRFLAC_ERROR;
58173 case EOWNERDEAD: return DRFLAC_ERROR;
58175 #ifdef ENOTRECOVERABLE
58176 case ENOTRECOVERABLE: return DRFLAC_ERROR;
58179 case ERFKILL: return DRFLAC_ERROR;
58182 case EHWPOISON: return DRFLAC_ERROR;
58184 default: return DRFLAC_ERROR;
58187 static drflac_result drflac_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
58189 #if defined(_MSC_VER) && _MSC_VER >= 1400
58192 if (ppFile != NULL) {
58195 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
58196 return DRFLAC_INVALID_ARGS;
58198 #if defined(_MSC_VER) && _MSC_VER >= 1400
58199 err = fopen_s(ppFile, pFilePath, pOpenMode);
58201 return drflac_result_from_errno(err);
58204 #if defined(_WIN32) || defined(__APPLE__)
58205 *ppFile = fopen(pFilePath, pOpenMode);
58207 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
58208 *ppFile = fopen64(pFilePath, pOpenMode);
58210 *ppFile = fopen(pFilePath, pOpenMode);
58213 if (*ppFile == NULL) {
58214 drflac_result result = drflac_result_from_errno(errno);
58215 if (result == DRFLAC_SUCCESS) {
58216 result = DRFLAC_ERROR;
58221 return DRFLAC_SUCCESS;
58223 #if defined(_WIN32)
58224 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
58225 #define DRFLAC_HAS_WFOPEN
58228 static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drflac_allocation_callbacks* pAllocationCallbacks)
58230 if (ppFile != NULL) {
58233 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
58234 return DRFLAC_INVALID_ARGS;
58236 #if defined(DRFLAC_HAS_WFOPEN)
58238 #if defined(_MSC_VER) && _MSC_VER >= 1400
58239 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
58241 return drflac_result_from_errno(err);
58244 *ppFile = _wfopen(pFilePath, pOpenMode);
58245 if (*ppFile == NULL) {
58246 return drflac_result_from_errno(errno);
58249 (void)pAllocationCallbacks;
58255 const wchar_t* pFilePathTemp = pFilePath;
58256 char* pFilePathMB = NULL;
58257 char pOpenModeMB[32] = {0};
58258 DRFLAC_ZERO_OBJECT(&mbs);
58259 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
58260 if (lenMB == (size_t)-1) {
58261 return drflac_result_from_errno(errno);
58263 pFilePathMB = (char*)drflac__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);
58264 if (pFilePathMB == NULL) {
58265 return DRFLAC_OUT_OF_MEMORY;
58267 pFilePathTemp = pFilePath;
58268 DRFLAC_ZERO_OBJECT(&mbs);
58269 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
58273 if (pOpenMode[i] == 0) {
58274 pOpenModeMB[i] = '\0';
58277 pOpenModeMB[i] = (char)pOpenMode[i];
58281 *ppFile = fopen(pFilePathMB, pOpenModeMB);
58282 drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
58284 if (*ppFile == NULL) {
58285 return DRFLAC_ERROR;
58288 return DRFLAC_SUCCESS;
58290 static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
58292 return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData);
58294 static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin)
58296 DRFLAC_ASSERT(offset >= 0);
58297 return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
58299 DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks)
58303 if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) {
58306 pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
58307 if (pFlac == NULL) {
58313 DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks)
58317 if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) {
58320 pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
58321 if (pFlac == NULL) {
58327 DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
58331 if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) {
58334 pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
58335 if (pFlac == NULL) {
58341 DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
58345 if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) {
58348 pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
58349 if (pFlac == NULL) {
58356 static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead)
58358 drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
58359 size_t bytesRemaining;
58360 DRFLAC_ASSERT(memoryStream != NULL);
58361 DRFLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos);
58362 bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos;
58363 if (bytesToRead > bytesRemaining) {
58364 bytesToRead = bytesRemaining;
58366 if (bytesToRead > 0) {
58367 DRFLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead);
58368 memoryStream->currentReadPos += bytesToRead;
58370 return bytesToRead;
58372 static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin)
58374 drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
58375 DRFLAC_ASSERT(memoryStream != NULL);
58376 DRFLAC_ASSERT(offset >= 0);
58377 if (offset > (drflac_int64)memoryStream->dataSize) {
58378 return DRFLAC_FALSE;
58380 if (origin == drflac_seek_origin_current) {
58381 if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) {
58382 memoryStream->currentReadPos += offset;
58384 return DRFLAC_FALSE;
58387 if ((drflac_uint32)offset <= memoryStream->dataSize) {
58388 memoryStream->currentReadPos = offset;
58390 return DRFLAC_FALSE;
58393 return DRFLAC_TRUE;
58395 DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks)
58397 drflac__memory_stream memoryStream;
58399 memoryStream.data = (const drflac_uint8*)pData;
58400 memoryStream.dataSize = dataSize;
58401 memoryStream.currentReadPos = 0;
58402 pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks);
58403 if (pFlac == NULL) {
58406 pFlac->memoryStream = memoryStream;
58407 #ifndef DR_FLAC_NO_OGG
58408 if (pFlac->container == drflac_container_ogg)
58410 drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
58411 oggbs->pUserData = &pFlac->memoryStream;
58416 pFlac->bs.pUserData = &pFlac->memoryStream;
58420 DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
58422 drflac__memory_stream memoryStream;
58424 memoryStream.data = (const drflac_uint8*)pData;
58425 memoryStream.dataSize = dataSize;
58426 memoryStream.currentReadPos = 0;
58427 pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks);
58428 if (pFlac == NULL) {
58431 pFlac->memoryStream = memoryStream;
58432 #ifndef DR_FLAC_NO_OGG
58433 if (pFlac->container == drflac_container_ogg)
58435 drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
58436 oggbs->pUserData = &pFlac->memoryStream;
58441 pFlac->bs.pUserData = &pFlac->memoryStream;
58445 DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
58447 return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
58449 DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
58451 return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks);
58453 DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
58455 return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
58457 DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
58459 return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks);
58461 DRFLAC_API void drflac_close(drflac* pFlac)
58463 if (pFlac == NULL) {
58466 #ifndef DR_FLAC_NO_STDIO
58467 if (pFlac->bs.onRead == drflac__on_read_stdio) {
58468 fclose((FILE*)pFlac->bs.pUserData);
58470 #ifndef DR_FLAC_NO_OGG
58471 if (pFlac->container == drflac_container_ogg) {
58472 drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
58473 DRFLAC_ASSERT(pFlac->bs.onRead == drflac__on_read_ogg);
58474 if (oggbs->onRead == drflac__on_read_stdio) {
58475 fclose((FILE*)oggbs->pUserData);
58480 drflac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks);
58483 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
58486 for (i = 0; i < frameCount; ++i) {
58487 drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
58488 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
58489 drflac_uint32 right = left - side;
58490 pOutputSamples[i*2+0] = (drflac_int32)left;
58491 pOutputSamples[i*2+1] = (drflac_int32)right;
58495 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
58498 drflac_uint64 frameCount4 = frameCount >> 2;
58499 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
58500 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
58501 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58502 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58503 for (i = 0; i < frameCount4; ++i) {
58504 drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
58505 drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
58506 drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
58507 drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
58508 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
58509 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
58510 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
58511 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
58512 drflac_uint32 right0 = left0 - side0;
58513 drflac_uint32 right1 = left1 - side1;
58514 drflac_uint32 right2 = left2 - side2;
58515 drflac_uint32 right3 = left3 - side3;
58516 pOutputSamples[i*8+0] = (drflac_int32)left0;
58517 pOutputSamples[i*8+1] = (drflac_int32)right0;
58518 pOutputSamples[i*8+2] = (drflac_int32)left1;
58519 pOutputSamples[i*8+3] = (drflac_int32)right1;
58520 pOutputSamples[i*8+4] = (drflac_int32)left2;
58521 pOutputSamples[i*8+5] = (drflac_int32)right2;
58522 pOutputSamples[i*8+6] = (drflac_int32)left3;
58523 pOutputSamples[i*8+7] = (drflac_int32)right3;
58525 for (i = (frameCount4 << 2); i < frameCount; ++i) {
58526 drflac_uint32 left = pInputSamples0U32[i] << shift0;
58527 drflac_uint32 side = pInputSamples1U32[i] << shift1;
58528 drflac_uint32 right = left - side;
58529 pOutputSamples[i*2+0] = (drflac_int32)left;
58530 pOutputSamples[i*2+1] = (drflac_int32)right;
58533 #if defined(DRFLAC_SUPPORT_SSE2)
58534 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
58537 drflac_uint64 frameCount4 = frameCount >> 2;
58538 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
58539 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
58540 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58541 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58542 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
58543 for (i = 0; i < frameCount4; ++i) {
58544 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
58545 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
58546 __m128i right = _mm_sub_epi32(left, side);
58547 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
58548 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
58550 for (i = (frameCount4 << 2); i < frameCount; ++i) {
58551 drflac_uint32 left = pInputSamples0U32[i] << shift0;
58552 drflac_uint32 side = pInputSamples1U32[i] << shift1;
58553 drflac_uint32 right = left - side;
58554 pOutputSamples[i*2+0] = (drflac_int32)left;
58555 pOutputSamples[i*2+1] = (drflac_int32)right;
58559 #if defined(DRFLAC_SUPPORT_NEON)
58560 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
58563 drflac_uint64 frameCount4 = frameCount >> 2;
58564 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
58565 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
58566 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58567 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58568 int32x4_t shift0_4;
58569 int32x4_t shift1_4;
58570 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
58571 shift0_4 = vdupq_n_s32(shift0);
58572 shift1_4 = vdupq_n_s32(shift1);
58573 for (i = 0; i < frameCount4; ++i) {
58577 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
58578 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
58579 right = vsubq_u32(left, side);
58580 drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right));
58582 for (i = (frameCount4 << 2); i < frameCount; ++i) {
58583 drflac_uint32 left = pInputSamples0U32[i] << shift0;
58584 drflac_uint32 side = pInputSamples1U32[i] << shift1;
58585 drflac_uint32 right = left - side;
58586 pOutputSamples[i*2+0] = (drflac_int32)left;
58587 pOutputSamples[i*2+1] = (drflac_int32)right;
58591 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
58593 #if defined(DRFLAC_SUPPORT_SSE2)
58594 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
58595 drflac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
58597 #elif defined(DRFLAC_SUPPORT_NEON)
58598 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
58599 drflac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
58604 drflac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
58606 drflac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
58611 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
58614 for (i = 0; i < frameCount; ++i) {
58615 drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
58616 drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
58617 drflac_uint32 left = right + side;
58618 pOutputSamples[i*2+0] = (drflac_int32)left;
58619 pOutputSamples[i*2+1] = (drflac_int32)right;
58623 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
58626 drflac_uint64 frameCount4 = frameCount >> 2;
58627 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
58628 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
58629 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58630 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58631 for (i = 0; i < frameCount4; ++i) {
58632 drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
58633 drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
58634 drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
58635 drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
58636 drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
58637 drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
58638 drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
58639 drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
58640 drflac_uint32 left0 = right0 + side0;
58641 drflac_uint32 left1 = right1 + side1;
58642 drflac_uint32 left2 = right2 + side2;
58643 drflac_uint32 left3 = right3 + side3;
58644 pOutputSamples[i*8+0] = (drflac_int32)left0;
58645 pOutputSamples[i*8+1] = (drflac_int32)right0;
58646 pOutputSamples[i*8+2] = (drflac_int32)left1;
58647 pOutputSamples[i*8+3] = (drflac_int32)right1;
58648 pOutputSamples[i*8+4] = (drflac_int32)left2;
58649 pOutputSamples[i*8+5] = (drflac_int32)right2;
58650 pOutputSamples[i*8+6] = (drflac_int32)left3;
58651 pOutputSamples[i*8+7] = (drflac_int32)right3;
58653 for (i = (frameCount4 << 2); i < frameCount; ++i) {
58654 drflac_uint32 side = pInputSamples0U32[i] << shift0;
58655 drflac_uint32 right = pInputSamples1U32[i] << shift1;
58656 drflac_uint32 left = right + side;
58657 pOutputSamples[i*2+0] = (drflac_int32)left;
58658 pOutputSamples[i*2+1] = (drflac_int32)right;
58661 #if defined(DRFLAC_SUPPORT_SSE2)
58662 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
58665 drflac_uint64 frameCount4 = frameCount >> 2;
58666 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
58667 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
58668 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58669 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58670 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
58671 for (i = 0; i < frameCount4; ++i) {
58672 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
58673 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
58674 __m128i left = _mm_add_epi32(right, side);
58675 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
58676 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
58678 for (i = (frameCount4 << 2); i < frameCount; ++i) {
58679 drflac_uint32 side = pInputSamples0U32[i] << shift0;
58680 drflac_uint32 right = pInputSamples1U32[i] << shift1;
58681 drflac_uint32 left = right + side;
58682 pOutputSamples[i*2+0] = (drflac_int32)left;
58683 pOutputSamples[i*2+1] = (drflac_int32)right;
58687 #if defined(DRFLAC_SUPPORT_NEON)
58688 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
58691 drflac_uint64 frameCount4 = frameCount >> 2;
58692 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
58693 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
58694 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58695 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58696 int32x4_t shift0_4;
58697 int32x4_t shift1_4;
58698 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
58699 shift0_4 = vdupq_n_s32(shift0);
58700 shift1_4 = vdupq_n_s32(shift1);
58701 for (i = 0; i < frameCount4; ++i) {
58705 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
58706 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
58707 left = vaddq_u32(right, side);
58708 drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right));
58710 for (i = (frameCount4 << 2); i < frameCount; ++i) {
58711 drflac_uint32 side = pInputSamples0U32[i] << shift0;
58712 drflac_uint32 right = pInputSamples1U32[i] << shift1;
58713 drflac_uint32 left = right + side;
58714 pOutputSamples[i*2+0] = (drflac_int32)left;
58715 pOutputSamples[i*2+1] = (drflac_int32)right;
58719 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
58721 #if defined(DRFLAC_SUPPORT_SSE2)
58722 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
58723 drflac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
58725 #elif defined(DRFLAC_SUPPORT_NEON)
58726 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
58727 drflac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
58732 drflac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
58734 drflac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
58739 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
58741 for (drflac_uint64 i = 0; i < frameCount; ++i) {
58742 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58743 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58744 mid = (mid << 1) | (side & 0x01);
58745 pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample);
58746 pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample);
58750 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
58753 drflac_uint64 frameCount4 = frameCount >> 2;
58754 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
58755 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
58756 drflac_int32 shift = unusedBitsPerSample;
58759 for (i = 0; i < frameCount4; ++i) {
58760 drflac_uint32 temp0L;
58761 drflac_uint32 temp1L;
58762 drflac_uint32 temp2L;
58763 drflac_uint32 temp3L;
58764 drflac_uint32 temp0R;
58765 drflac_uint32 temp1R;
58766 drflac_uint32 temp2R;
58767 drflac_uint32 temp3R;
58768 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58769 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58770 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58771 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58772 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58773 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58774 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58775 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58776 mid0 = (mid0 << 1) | (side0 & 0x01);
58777 mid1 = (mid1 << 1) | (side1 & 0x01);
58778 mid2 = (mid2 << 1) | (side2 & 0x01);
58779 mid3 = (mid3 << 1) | (side3 & 0x01);
58780 temp0L = (mid0 + side0) << shift;
58781 temp1L = (mid1 + side1) << shift;
58782 temp2L = (mid2 + side2) << shift;
58783 temp3L = (mid3 + side3) << shift;
58784 temp0R = (mid0 - side0) << shift;
58785 temp1R = (mid1 - side1) << shift;
58786 temp2R = (mid2 - side2) << shift;
58787 temp3R = (mid3 - side3) << shift;
58788 pOutputSamples[i*8+0] = (drflac_int32)temp0L;
58789 pOutputSamples[i*8+1] = (drflac_int32)temp0R;
58790 pOutputSamples[i*8+2] = (drflac_int32)temp1L;
58791 pOutputSamples[i*8+3] = (drflac_int32)temp1R;
58792 pOutputSamples[i*8+4] = (drflac_int32)temp2L;
58793 pOutputSamples[i*8+5] = (drflac_int32)temp2R;
58794 pOutputSamples[i*8+6] = (drflac_int32)temp3L;
58795 pOutputSamples[i*8+7] = (drflac_int32)temp3R;
58798 for (i = 0; i < frameCount4; ++i) {
58799 drflac_uint32 temp0L;
58800 drflac_uint32 temp1L;
58801 drflac_uint32 temp2L;
58802 drflac_uint32 temp3L;
58803 drflac_uint32 temp0R;
58804 drflac_uint32 temp1R;
58805 drflac_uint32 temp2R;
58806 drflac_uint32 temp3R;
58807 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58808 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58809 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58810 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58811 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58812 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58813 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58814 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58815 mid0 = (mid0 << 1) | (side0 & 0x01);
58816 mid1 = (mid1 << 1) | (side1 & 0x01);
58817 mid2 = (mid2 << 1) | (side2 & 0x01);
58818 mid3 = (mid3 << 1) | (side3 & 0x01);
58819 temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1);
58820 temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1);
58821 temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1);
58822 temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1);
58823 temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1);
58824 temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1);
58825 temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1);
58826 temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1);
58827 pOutputSamples[i*8+0] = (drflac_int32)temp0L;
58828 pOutputSamples[i*8+1] = (drflac_int32)temp0R;
58829 pOutputSamples[i*8+2] = (drflac_int32)temp1L;
58830 pOutputSamples[i*8+3] = (drflac_int32)temp1R;
58831 pOutputSamples[i*8+4] = (drflac_int32)temp2L;
58832 pOutputSamples[i*8+5] = (drflac_int32)temp2R;
58833 pOutputSamples[i*8+6] = (drflac_int32)temp3L;
58834 pOutputSamples[i*8+7] = (drflac_int32)temp3R;
58837 for (i = (frameCount4 << 2); i < frameCount; ++i) {
58838 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58839 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58840 mid = (mid << 1) | (side & 0x01);
58841 pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample);
58842 pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample);
58845 #if defined(DRFLAC_SUPPORT_SSE2)
58846 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
58849 drflac_uint64 frameCount4 = frameCount >> 2;
58850 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
58851 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
58852 drflac_int32 shift = unusedBitsPerSample;
58853 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
58855 for (i = 0; i < frameCount4; ++i) {
58860 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
58861 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
58862 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
58863 left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
58864 right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
58865 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
58866 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
58868 for (i = (frameCount4 << 2); i < frameCount; ++i) {
58869 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58870 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58871 mid = (mid << 1) | (side & 0x01);
58872 pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1;
58873 pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1;
58877 for (i = 0; i < frameCount4; ++i) {
58882 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
58883 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
58884 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
58885 left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
58886 right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
58887 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
58888 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
58890 for (i = (frameCount4 << 2); i < frameCount; ++i) {
58891 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58892 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58893 mid = (mid << 1) | (side & 0x01);
58894 pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift);
58895 pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift);
58900 #if defined(DRFLAC_SUPPORT_NEON)
58901 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
58904 drflac_uint64 frameCount4 = frameCount >> 2;
58905 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
58906 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
58907 drflac_int32 shift = unusedBitsPerSample;
58908 int32x4_t wbpsShift0_4;
58909 int32x4_t wbpsShift1_4;
58911 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
58912 wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
58913 wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
58914 one4 = vdupq_n_u32(1);
58916 for (i = 0; i < frameCount4; ++i) {
58921 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
58922 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
58923 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4));
58924 left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
58925 right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
58926 drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
58928 for (i = (frameCount4 << 2); i < frameCount; ++i) {
58929 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58930 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58931 mid = (mid << 1) | (side & 0x01);
58932 pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1;
58933 pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1;
58938 shift4 = vdupq_n_s32(shift);
58939 for (i = 0; i < frameCount4; ++i) {
58944 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
58945 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
58946 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4));
58947 left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
58948 right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
58949 drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
58951 for (i = (frameCount4 << 2); i < frameCount; ++i) {
58952 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58953 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58954 mid = (mid << 1) | (side & 0x01);
58955 pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift);
58956 pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift);
58961 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
58963 #if defined(DRFLAC_SUPPORT_SSE2)
58964 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
58965 drflac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
58967 #elif defined(DRFLAC_SUPPORT_NEON)
58968 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
58969 drflac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
58974 drflac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
58976 drflac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
58981 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
58983 for (drflac_uint64 i = 0; i < frameCount; ++i) {
58984 pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample));
58985 pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample));
58989 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
58992 drflac_uint64 frameCount4 = frameCount >> 2;
58993 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
58994 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
58995 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
58996 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
58997 for (i = 0; i < frameCount4; ++i) {
58998 drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
58999 drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
59000 drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
59001 drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
59002 drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
59003 drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
59004 drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
59005 drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
59006 pOutputSamples[i*8+0] = (drflac_int32)tempL0;
59007 pOutputSamples[i*8+1] = (drflac_int32)tempR0;
59008 pOutputSamples[i*8+2] = (drflac_int32)tempL1;
59009 pOutputSamples[i*8+3] = (drflac_int32)tempR1;
59010 pOutputSamples[i*8+4] = (drflac_int32)tempL2;
59011 pOutputSamples[i*8+5] = (drflac_int32)tempR2;
59012 pOutputSamples[i*8+6] = (drflac_int32)tempL3;
59013 pOutputSamples[i*8+7] = (drflac_int32)tempR3;
59015 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59016 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0);
59017 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1);
59020 #if defined(DRFLAC_SUPPORT_SSE2)
59021 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
59024 drflac_uint64 frameCount4 = frameCount >> 2;
59025 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
59026 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
59027 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59028 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59029 for (i = 0; i < frameCount4; ++i) {
59030 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
59031 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
59032 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
59033 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
59035 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59036 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0);
59037 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1);
59041 #if defined(DRFLAC_SUPPORT_NEON)
59042 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
59045 drflac_uint64 frameCount4 = frameCount >> 2;
59046 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
59047 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
59048 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59049 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59050 int32x4_t shift4_0 = vdupq_n_s32(shift0);
59051 int32x4_t shift4_1 = vdupq_n_s32(shift1);
59052 for (i = 0; i < frameCount4; ++i) {
59055 left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0));
59056 right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1));
59057 drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
59059 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59060 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0);
59061 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1);
59065 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
59067 #if defined(DRFLAC_SUPPORT_SSE2)
59068 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
59069 drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59071 #elif defined(DRFLAC_SUPPORT_NEON)
59072 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
59073 drflac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59078 drflac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59080 drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59084 DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut)
59086 drflac_uint64 framesRead;
59087 drflac_uint32 unusedBitsPerSample;
59088 if (pFlac == NULL || framesToRead == 0) {
59091 if (pBufferOut == NULL) {
59092 return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead);
59094 DRFLAC_ASSERT(pFlac->bitsPerSample <= 32);
59095 unusedBitsPerSample = 32 - pFlac->bitsPerSample;
59097 while (framesToRead > 0) {
59098 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
59099 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
59103 unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
59104 drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
59105 drflac_uint64 frameCountThisIteration = framesToRead;
59106 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
59107 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
59109 if (channelCount == 2) {
59110 const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
59111 const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
59112 switch (pFlac->currentFLACFrame.header.channelAssignment)
59114 case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
59116 drflac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
59118 case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
59120 drflac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
59122 case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
59124 drflac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
59126 case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
59129 drflac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
59134 for (i = 0; i < frameCountThisIteration; ++i) {
59136 for (j = 0; j < channelCount; ++j) {
59137 pBufferOut[(i*channelCount)+j] = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
59141 framesRead += frameCountThisIteration;
59142 pBufferOut += frameCountThisIteration * channelCount;
59143 framesToRead -= frameCountThisIteration;
59144 pFlac->currentPCMFrame += frameCountThisIteration;
59145 pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration;
59151 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59154 for (i = 0; i < frameCount; ++i) {
59155 drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
59156 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
59157 drflac_uint32 right = left - side;
59160 pOutputSamples[i*2+0] = (drflac_int16)left;
59161 pOutputSamples[i*2+1] = (drflac_int16)right;
59165 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59168 drflac_uint64 frameCount4 = frameCount >> 2;
59169 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
59170 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
59171 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59172 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59173 for (i = 0; i < frameCount4; ++i) {
59174 drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
59175 drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
59176 drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
59177 drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
59178 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
59179 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
59180 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
59181 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
59182 drflac_uint32 right0 = left0 - side0;
59183 drflac_uint32 right1 = left1 - side1;
59184 drflac_uint32 right2 = left2 - side2;
59185 drflac_uint32 right3 = left3 - side3;
59194 pOutputSamples[i*8+0] = (drflac_int16)left0;
59195 pOutputSamples[i*8+1] = (drflac_int16)right0;
59196 pOutputSamples[i*8+2] = (drflac_int16)left1;
59197 pOutputSamples[i*8+3] = (drflac_int16)right1;
59198 pOutputSamples[i*8+4] = (drflac_int16)left2;
59199 pOutputSamples[i*8+5] = (drflac_int16)right2;
59200 pOutputSamples[i*8+6] = (drflac_int16)left3;
59201 pOutputSamples[i*8+7] = (drflac_int16)right3;
59203 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59204 drflac_uint32 left = pInputSamples0U32[i] << shift0;
59205 drflac_uint32 side = pInputSamples1U32[i] << shift1;
59206 drflac_uint32 right = left - side;
59209 pOutputSamples[i*2+0] = (drflac_int16)left;
59210 pOutputSamples[i*2+1] = (drflac_int16)right;
59213 #if defined(DRFLAC_SUPPORT_SSE2)
59214 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59217 drflac_uint64 frameCount4 = frameCount >> 2;
59218 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
59219 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
59220 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59221 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59222 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
59223 for (i = 0; i < frameCount4; ++i) {
59224 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
59225 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
59226 __m128i right = _mm_sub_epi32(left, side);
59227 left = _mm_srai_epi32(left, 16);
59228 right = _mm_srai_epi32(right, 16);
59229 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
59231 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59232 drflac_uint32 left = pInputSamples0U32[i] << shift0;
59233 drflac_uint32 side = pInputSamples1U32[i] << shift1;
59234 drflac_uint32 right = left - side;
59237 pOutputSamples[i*2+0] = (drflac_int16)left;
59238 pOutputSamples[i*2+1] = (drflac_int16)right;
59242 #if defined(DRFLAC_SUPPORT_NEON)
59243 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59246 drflac_uint64 frameCount4 = frameCount >> 2;
59247 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
59248 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
59249 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59250 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59251 int32x4_t shift0_4;
59252 int32x4_t shift1_4;
59253 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
59254 shift0_4 = vdupq_n_s32(shift0);
59255 shift1_4 = vdupq_n_s32(shift1);
59256 for (i = 0; i < frameCount4; ++i) {
59260 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
59261 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
59262 right = vsubq_u32(left, side);
59263 left = vshrq_n_u32(left, 16);
59264 right = vshrq_n_u32(right, 16);
59265 drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right)));
59267 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59268 drflac_uint32 left = pInputSamples0U32[i] << shift0;
59269 drflac_uint32 side = pInputSamples1U32[i] << shift1;
59270 drflac_uint32 right = left - side;
59273 pOutputSamples[i*2+0] = (drflac_int16)left;
59274 pOutputSamples[i*2+1] = (drflac_int16)right;
59278 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59280 #if defined(DRFLAC_SUPPORT_SSE2)
59281 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
59282 drflac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59284 #elif defined(DRFLAC_SUPPORT_NEON)
59285 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
59286 drflac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59291 drflac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59293 drflac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59298 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59301 for (i = 0; i < frameCount; ++i) {
59302 drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
59303 drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
59304 drflac_uint32 left = right + side;
59307 pOutputSamples[i*2+0] = (drflac_int16)left;
59308 pOutputSamples[i*2+1] = (drflac_int16)right;
59312 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59315 drflac_uint64 frameCount4 = frameCount >> 2;
59316 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
59317 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
59318 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59319 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59320 for (i = 0; i < frameCount4; ++i) {
59321 drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
59322 drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
59323 drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
59324 drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
59325 drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
59326 drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
59327 drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
59328 drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
59329 drflac_uint32 left0 = right0 + side0;
59330 drflac_uint32 left1 = right1 + side1;
59331 drflac_uint32 left2 = right2 + side2;
59332 drflac_uint32 left3 = right3 + side3;
59341 pOutputSamples[i*8+0] = (drflac_int16)left0;
59342 pOutputSamples[i*8+1] = (drflac_int16)right0;
59343 pOutputSamples[i*8+2] = (drflac_int16)left1;
59344 pOutputSamples[i*8+3] = (drflac_int16)right1;
59345 pOutputSamples[i*8+4] = (drflac_int16)left2;
59346 pOutputSamples[i*8+5] = (drflac_int16)right2;
59347 pOutputSamples[i*8+6] = (drflac_int16)left3;
59348 pOutputSamples[i*8+7] = (drflac_int16)right3;
59350 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59351 drflac_uint32 side = pInputSamples0U32[i] << shift0;
59352 drflac_uint32 right = pInputSamples1U32[i] << shift1;
59353 drflac_uint32 left = right + side;
59356 pOutputSamples[i*2+0] = (drflac_int16)left;
59357 pOutputSamples[i*2+1] = (drflac_int16)right;
59360 #if defined(DRFLAC_SUPPORT_SSE2)
59361 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59364 drflac_uint64 frameCount4 = frameCount >> 2;
59365 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
59366 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
59367 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59368 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59369 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
59370 for (i = 0; i < frameCount4; ++i) {
59371 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
59372 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
59373 __m128i left = _mm_add_epi32(right, side);
59374 left = _mm_srai_epi32(left, 16);
59375 right = _mm_srai_epi32(right, 16);
59376 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
59378 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59379 drflac_uint32 side = pInputSamples0U32[i] << shift0;
59380 drflac_uint32 right = pInputSamples1U32[i] << shift1;
59381 drflac_uint32 left = right + side;
59384 pOutputSamples[i*2+0] = (drflac_int16)left;
59385 pOutputSamples[i*2+1] = (drflac_int16)right;
59389 #if defined(DRFLAC_SUPPORT_NEON)
59390 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59393 drflac_uint64 frameCount4 = frameCount >> 2;
59394 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
59395 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
59396 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59397 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59398 int32x4_t shift0_4;
59399 int32x4_t shift1_4;
59400 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
59401 shift0_4 = vdupq_n_s32(shift0);
59402 shift1_4 = vdupq_n_s32(shift1);
59403 for (i = 0; i < frameCount4; ++i) {
59407 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
59408 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
59409 left = vaddq_u32(right, side);
59410 left = vshrq_n_u32(left, 16);
59411 right = vshrq_n_u32(right, 16);
59412 drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right)));
59414 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59415 drflac_uint32 side = pInputSamples0U32[i] << shift0;
59416 drflac_uint32 right = pInputSamples1U32[i] << shift1;
59417 drflac_uint32 left = right + side;
59420 pOutputSamples[i*2+0] = (drflac_int16)left;
59421 pOutputSamples[i*2+1] = (drflac_int16)right;
59425 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59427 #if defined(DRFLAC_SUPPORT_SSE2)
59428 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
59429 drflac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59431 #elif defined(DRFLAC_SUPPORT_NEON)
59432 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
59433 drflac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59438 drflac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59440 drflac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59445 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59447 for (drflac_uint64 i = 0; i < frameCount; ++i) {
59448 drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59449 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59450 mid = (mid << 1) | (side & 0x01);
59451 pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16);
59452 pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16);
59456 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59459 drflac_uint64 frameCount4 = frameCount >> 2;
59460 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
59461 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
59462 drflac_uint32 shift = unusedBitsPerSample;
59465 for (i = 0; i < frameCount4; ++i) {
59466 drflac_uint32 temp0L;
59467 drflac_uint32 temp1L;
59468 drflac_uint32 temp2L;
59469 drflac_uint32 temp3L;
59470 drflac_uint32 temp0R;
59471 drflac_uint32 temp1R;
59472 drflac_uint32 temp2R;
59473 drflac_uint32 temp3R;
59474 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59475 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59476 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59477 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59478 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59479 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59480 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59481 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59482 mid0 = (mid0 << 1) | (side0 & 0x01);
59483 mid1 = (mid1 << 1) | (side1 & 0x01);
59484 mid2 = (mid2 << 1) | (side2 & 0x01);
59485 mid3 = (mid3 << 1) | (side3 & 0x01);
59486 temp0L = (mid0 + side0) << shift;
59487 temp1L = (mid1 + side1) << shift;
59488 temp2L = (mid2 + side2) << shift;
59489 temp3L = (mid3 + side3) << shift;
59490 temp0R = (mid0 - side0) << shift;
59491 temp1R = (mid1 - side1) << shift;
59492 temp2R = (mid2 - side2) << shift;
59493 temp3R = (mid3 - side3) << shift;
59502 pOutputSamples[i*8+0] = (drflac_int16)temp0L;
59503 pOutputSamples[i*8+1] = (drflac_int16)temp0R;
59504 pOutputSamples[i*8+2] = (drflac_int16)temp1L;
59505 pOutputSamples[i*8+3] = (drflac_int16)temp1R;
59506 pOutputSamples[i*8+4] = (drflac_int16)temp2L;
59507 pOutputSamples[i*8+5] = (drflac_int16)temp2R;
59508 pOutputSamples[i*8+6] = (drflac_int16)temp3L;
59509 pOutputSamples[i*8+7] = (drflac_int16)temp3R;
59512 for (i = 0; i < frameCount4; ++i) {
59513 drflac_uint32 temp0L;
59514 drflac_uint32 temp1L;
59515 drflac_uint32 temp2L;
59516 drflac_uint32 temp3L;
59517 drflac_uint32 temp0R;
59518 drflac_uint32 temp1R;
59519 drflac_uint32 temp2R;
59520 drflac_uint32 temp3R;
59521 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59522 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59523 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59524 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59525 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59526 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59527 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59528 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59529 mid0 = (mid0 << 1) | (side0 & 0x01);
59530 mid1 = (mid1 << 1) | (side1 & 0x01);
59531 mid2 = (mid2 << 1) | (side2 & 0x01);
59532 mid3 = (mid3 << 1) | (side3 & 0x01);
59533 temp0L = ((drflac_int32)(mid0 + side0) >> 1);
59534 temp1L = ((drflac_int32)(mid1 + side1) >> 1);
59535 temp2L = ((drflac_int32)(mid2 + side2) >> 1);
59536 temp3L = ((drflac_int32)(mid3 + side3) >> 1);
59537 temp0R = ((drflac_int32)(mid0 - side0) >> 1);
59538 temp1R = ((drflac_int32)(mid1 - side1) >> 1);
59539 temp2R = ((drflac_int32)(mid2 - side2) >> 1);
59540 temp3R = ((drflac_int32)(mid3 - side3) >> 1);
59549 pOutputSamples[i*8+0] = (drflac_int16)temp0L;
59550 pOutputSamples[i*8+1] = (drflac_int16)temp0R;
59551 pOutputSamples[i*8+2] = (drflac_int16)temp1L;
59552 pOutputSamples[i*8+3] = (drflac_int16)temp1R;
59553 pOutputSamples[i*8+4] = (drflac_int16)temp2L;
59554 pOutputSamples[i*8+5] = (drflac_int16)temp2R;
59555 pOutputSamples[i*8+6] = (drflac_int16)temp3L;
59556 pOutputSamples[i*8+7] = (drflac_int16)temp3R;
59559 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59560 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59561 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59562 mid = (mid << 1) | (side & 0x01);
59563 pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16);
59564 pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16);
59567 #if defined(DRFLAC_SUPPORT_SSE2)
59568 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59571 drflac_uint64 frameCount4 = frameCount >> 2;
59572 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
59573 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
59574 drflac_uint32 shift = unusedBitsPerSample;
59575 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
59577 for (i = 0; i < frameCount4; ++i) {
59582 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
59583 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
59584 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
59585 left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
59586 right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
59587 left = _mm_srai_epi32(left, 16);
59588 right = _mm_srai_epi32(right, 16);
59589 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
59591 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59592 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59593 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59594 mid = (mid << 1) | (side & 0x01);
59595 pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16);
59596 pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16);
59600 for (i = 0; i < frameCount4; ++i) {
59605 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
59606 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
59607 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
59608 left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
59609 right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
59610 left = _mm_srai_epi32(left, 16);
59611 right = _mm_srai_epi32(right, 16);
59612 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
59614 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59615 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59616 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59617 mid = (mid << 1) | (side & 0x01);
59618 pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16);
59619 pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16);
59624 #if defined(DRFLAC_SUPPORT_NEON)
59625 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59628 drflac_uint64 frameCount4 = frameCount >> 2;
59629 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
59630 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
59631 drflac_uint32 shift = unusedBitsPerSample;
59632 int32x4_t wbpsShift0_4;
59633 int32x4_t wbpsShift1_4;
59634 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
59635 wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
59636 wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
59638 for (i = 0; i < frameCount4; ++i) {
59643 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
59644 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
59645 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
59646 left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
59647 right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
59648 left = vshrq_n_s32(left, 16);
59649 right = vshrq_n_s32(right, 16);
59650 drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
59652 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59653 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59654 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59655 mid = (mid << 1) | (side & 0x01);
59656 pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16);
59657 pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16);
59662 shift4 = vdupq_n_s32(shift);
59663 for (i = 0; i < frameCount4; ++i) {
59668 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
59669 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
59670 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
59671 left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
59672 right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
59673 left = vshrq_n_s32(left, 16);
59674 right = vshrq_n_s32(right, 16);
59675 drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
59677 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59678 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59679 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59680 mid = (mid << 1) | (side & 0x01);
59681 pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16);
59682 pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16);
59687 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59689 #if defined(DRFLAC_SUPPORT_SSE2)
59690 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
59691 drflac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59693 #elif defined(DRFLAC_SUPPORT_NEON)
59694 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
59695 drflac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59700 drflac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59702 drflac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59707 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59709 for (drflac_uint64 i = 0; i < frameCount; ++i) {
59710 pOutputSamples[i*2+0] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16);
59711 pOutputSamples[i*2+1] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16);
59715 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59718 drflac_uint64 frameCount4 = frameCount >> 2;
59719 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
59720 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
59721 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59722 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59723 for (i = 0; i < frameCount4; ++i) {
59724 drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
59725 drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
59726 drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
59727 drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
59728 drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
59729 drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
59730 drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
59731 drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
59740 pOutputSamples[i*8+0] = (drflac_int16)tempL0;
59741 pOutputSamples[i*8+1] = (drflac_int16)tempR0;
59742 pOutputSamples[i*8+2] = (drflac_int16)tempL1;
59743 pOutputSamples[i*8+3] = (drflac_int16)tempR1;
59744 pOutputSamples[i*8+4] = (drflac_int16)tempL2;
59745 pOutputSamples[i*8+5] = (drflac_int16)tempR2;
59746 pOutputSamples[i*8+6] = (drflac_int16)tempL3;
59747 pOutputSamples[i*8+7] = (drflac_int16)tempR3;
59749 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59750 pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16);
59751 pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16);
59754 #if defined(DRFLAC_SUPPORT_SSE2)
59755 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59758 drflac_uint64 frameCount4 = frameCount >> 2;
59759 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
59760 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
59761 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59762 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59763 for (i = 0; i < frameCount4; ++i) {
59764 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
59765 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
59766 left = _mm_srai_epi32(left, 16);
59767 right = _mm_srai_epi32(right, 16);
59768 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
59770 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59771 pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16);
59772 pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16);
59776 #if defined(DRFLAC_SUPPORT_NEON)
59777 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59780 drflac_uint64 frameCount4 = frameCount >> 2;
59781 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
59782 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
59783 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59784 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59785 int32x4_t shift0_4 = vdupq_n_s32(shift0);
59786 int32x4_t shift1_4 = vdupq_n_s32(shift1);
59787 for (i = 0; i < frameCount4; ++i) {
59790 left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4));
59791 right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4));
59792 left = vshrq_n_s32(left, 16);
59793 right = vshrq_n_s32(right, 16);
59794 drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
59796 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59797 pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16);
59798 pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16);
59802 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
59804 #if defined(DRFLAC_SUPPORT_SSE2)
59805 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
59806 drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59808 #elif defined(DRFLAC_SUPPORT_NEON)
59809 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
59810 drflac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59815 drflac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59817 drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
59821 DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut)
59823 drflac_uint64 framesRead;
59824 drflac_uint32 unusedBitsPerSample;
59825 if (pFlac == NULL || framesToRead == 0) {
59828 if (pBufferOut == NULL) {
59829 return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead);
59831 DRFLAC_ASSERT(pFlac->bitsPerSample <= 32);
59832 unusedBitsPerSample = 32 - pFlac->bitsPerSample;
59834 while (framesToRead > 0) {
59835 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
59836 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
59840 unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
59841 drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
59842 drflac_uint64 frameCountThisIteration = framesToRead;
59843 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
59844 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
59846 if (channelCount == 2) {
59847 const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
59848 const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
59849 switch (pFlac->currentFLACFrame.header.channelAssignment)
59851 case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
59853 drflac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
59855 case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
59857 drflac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
59859 case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
59861 drflac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
59863 case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
59866 drflac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
59871 for (i = 0; i < frameCountThisIteration; ++i) {
59873 for (j = 0; j < channelCount; ++j) {
59874 drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
59875 pBufferOut[(i*channelCount)+j] = (drflac_int16)(sampleS32 >> 16);
59879 framesRead += frameCountThisIteration;
59880 pBufferOut += frameCountThisIteration * channelCount;
59881 framesToRead -= frameCountThisIteration;
59882 pFlac->currentPCMFrame += frameCountThisIteration;
59883 pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration;
59889 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
59892 for (i = 0; i < frameCount; ++i) {
59893 drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
59894 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
59895 drflac_uint32 right = left - side;
59896 pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0);
59897 pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0);
59901 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
59904 drflac_uint64 frameCount4 = frameCount >> 2;
59905 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
59906 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
59907 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
59908 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
59909 float factor = 1 / 2147483648.0;
59910 for (i = 0; i < frameCount4; ++i) {
59911 drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
59912 drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
59913 drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
59914 drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
59915 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
59916 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
59917 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
59918 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
59919 drflac_uint32 right0 = left0 - side0;
59920 drflac_uint32 right1 = left1 - side1;
59921 drflac_uint32 right2 = left2 - side2;
59922 drflac_uint32 right3 = left3 - side3;
59923 pOutputSamples[i*8+0] = (drflac_int32)left0 * factor;
59924 pOutputSamples[i*8+1] = (drflac_int32)right0 * factor;
59925 pOutputSamples[i*8+2] = (drflac_int32)left1 * factor;
59926 pOutputSamples[i*8+3] = (drflac_int32)right1 * factor;
59927 pOutputSamples[i*8+4] = (drflac_int32)left2 * factor;
59928 pOutputSamples[i*8+5] = (drflac_int32)right2 * factor;
59929 pOutputSamples[i*8+6] = (drflac_int32)left3 * factor;
59930 pOutputSamples[i*8+7] = (drflac_int32)right3 * factor;
59932 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59933 drflac_uint32 left = pInputSamples0U32[i] << shift0;
59934 drflac_uint32 side = pInputSamples1U32[i] << shift1;
59935 drflac_uint32 right = left - side;
59936 pOutputSamples[i*2+0] = (drflac_int32)left * factor;
59937 pOutputSamples[i*2+1] = (drflac_int32)right * factor;
59940 #if defined(DRFLAC_SUPPORT_SSE2)
59941 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
59944 drflac_uint64 frameCount4 = frameCount >> 2;
59945 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
59946 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
59947 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
59948 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
59950 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
59951 factor = _mm_set1_ps(1.0f / 8388608.0f);
59952 for (i = 0; i < frameCount4; ++i) {
59953 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
59954 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
59955 __m128i right = _mm_sub_epi32(left, side);
59956 __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
59957 __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
59958 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
59959 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
59961 for (i = (frameCount4 << 2); i < frameCount; ++i) {
59962 drflac_uint32 left = pInputSamples0U32[i] << shift0;
59963 drflac_uint32 side = pInputSamples1U32[i] << shift1;
59964 drflac_uint32 right = left - side;
59965 pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f;
59966 pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f;
59970 #if defined(DRFLAC_SUPPORT_NEON)
59971 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
59974 drflac_uint64 frameCount4 = frameCount >> 2;
59975 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
59976 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
59977 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
59978 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
59979 float32x4_t factor4;
59980 int32x4_t shift0_4;
59981 int32x4_t shift1_4;
59982 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
59983 factor4 = vdupq_n_f32(1.0f / 8388608.0f);
59984 shift0_4 = vdupq_n_s32(shift0);
59985 shift1_4 = vdupq_n_s32(shift1);
59986 for (i = 0; i < frameCount4; ++i) {
59991 float32x4_t rightf;
59992 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
59993 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
59994 right = vsubq_u32(left, side);
59995 leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4);
59996 rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4);
59997 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
59999 for (i = (frameCount4 << 2); i < frameCount; ++i) {
60000 drflac_uint32 left = pInputSamples0U32[i] << shift0;
60001 drflac_uint32 side = pInputSamples1U32[i] << shift1;
60002 drflac_uint32 right = left - side;
60003 pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f;
60004 pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f;
60008 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
60010 #if defined(DRFLAC_SUPPORT_SSE2)
60011 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
60012 drflac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
60014 #elif defined(DRFLAC_SUPPORT_NEON)
60015 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
60016 drflac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
60021 drflac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
60023 drflac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
60028 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
60031 for (i = 0; i < frameCount; ++i) {
60032 drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
60033 drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
60034 drflac_uint32 left = right + side;
60035 pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0);
60036 pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0);
60040 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
60043 drflac_uint64 frameCount4 = frameCount >> 2;
60044 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
60045 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
60046 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
60047 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
60048 float factor = 1 / 2147483648.0;
60049 for (i = 0; i < frameCount4; ++i) {
60050 drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
60051 drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
60052 drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
60053 drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
60054 drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
60055 drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
60056 drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
60057 drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
60058 drflac_uint32 left0 = right0 + side0;
60059 drflac_uint32 left1 = right1 + side1;
60060 drflac_uint32 left2 = right2 + side2;
60061 drflac_uint32 left3 = right3 + side3;
60062 pOutputSamples[i*8+0] = (drflac_int32)left0 * factor;
60063 pOutputSamples[i*8+1] = (drflac_int32)right0 * factor;
60064 pOutputSamples[i*8+2] = (drflac_int32)left1 * factor;
60065 pOutputSamples[i*8+3] = (drflac_int32)right1 * factor;
60066 pOutputSamples[i*8+4] = (drflac_int32)left2 * factor;
60067 pOutputSamples[i*8+5] = (drflac_int32)right2 * factor;
60068 pOutputSamples[i*8+6] = (drflac_int32)left3 * factor;
60069 pOutputSamples[i*8+7] = (drflac_int32)right3 * factor;
60071 for (i = (frameCount4 << 2); i < frameCount; ++i) {
60072 drflac_uint32 side = pInputSamples0U32[i] << shift0;
60073 drflac_uint32 right = pInputSamples1U32[i] << shift1;
60074 drflac_uint32 left = right + side;
60075 pOutputSamples[i*2+0] = (drflac_int32)left * factor;
60076 pOutputSamples[i*2+1] = (drflac_int32)right * factor;
60079 #if defined(DRFLAC_SUPPORT_SSE2)
60080 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
60083 drflac_uint64 frameCount4 = frameCount >> 2;
60084 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
60085 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
60086 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
60087 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
60089 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
60090 factor = _mm_set1_ps(1.0f / 8388608.0f);
60091 for (i = 0; i < frameCount4; ++i) {
60092 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
60093 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
60094 __m128i left = _mm_add_epi32(right, side);
60095 __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
60096 __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
60097 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
60098 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
60100 for (i = (frameCount4 << 2); i < frameCount; ++i) {
60101 drflac_uint32 side = pInputSamples0U32[i] << shift0;
60102 drflac_uint32 right = pInputSamples1U32[i] << shift1;
60103 drflac_uint32 left = right + side;
60104 pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f;
60105 pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f;
60109 #if defined(DRFLAC_SUPPORT_NEON)
60110 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
60113 drflac_uint64 frameCount4 = frameCount >> 2;
60114 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
60115 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
60116 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
60117 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
60118 float32x4_t factor4;
60119 int32x4_t shift0_4;
60120 int32x4_t shift1_4;
60121 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
60122 factor4 = vdupq_n_f32(1.0f / 8388608.0f);
60123 shift0_4 = vdupq_n_s32(shift0);
60124 shift1_4 = vdupq_n_s32(shift1);
60125 for (i = 0; i < frameCount4; ++i) {
60130 float32x4_t rightf;
60131 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
60132 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
60133 left = vaddq_u32(right, side);
60134 leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4);
60135 rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4);
60136 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
60138 for (i = (frameCount4 << 2); i < frameCount; ++i) {
60139 drflac_uint32 side = pInputSamples0U32[i] << shift0;
60140 drflac_uint32 right = pInputSamples1U32[i] << shift1;
60141 drflac_uint32 left = right + side;
60142 pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f;
60143 pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f;
60147 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
60149 #if defined(DRFLAC_SUPPORT_SSE2)
60150 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
60151 drflac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
60153 #elif defined(DRFLAC_SUPPORT_NEON)
60154 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
60155 drflac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
60160 drflac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
60162 drflac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
60167 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
60169 for (drflac_uint64 i = 0; i < frameCount; ++i) {
60170 drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
60171 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
60172 mid = (mid << 1) | (side & 0x01);
60173 pOutputSamples[i*2+0] = (float)((((drflac_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
60174 pOutputSamples[i*2+1] = (float)((((drflac_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
60178 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
60181 drflac_uint64 frameCount4 = frameCount >> 2;
60182 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
60183 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
60184 drflac_uint32 shift = unusedBitsPerSample;
60185 float factor = 1 / 2147483648.0;
60188 for (i = 0; i < frameCount4; ++i) {
60189 drflac_uint32 temp0L;
60190 drflac_uint32 temp1L;
60191 drflac_uint32 temp2L;
60192 drflac_uint32 temp3L;
60193 drflac_uint32 temp0R;
60194 drflac_uint32 temp1R;
60195 drflac_uint32 temp2R;
60196 drflac_uint32 temp3R;
60197 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
60198 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
60199 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
60200 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
60201 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
60202 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
60203 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
60204 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
60205 mid0 = (mid0 << 1) | (side0 & 0x01);
60206 mid1 = (mid1 << 1) | (side1 & 0x01);
60207 mid2 = (mid2 << 1) | (side2 & 0x01);
60208 mid3 = (mid3 << 1) | (side3 & 0x01);
60209 temp0L = (mid0 + side0) << shift;
60210 temp1L = (mid1 + side1) << shift;
60211 temp2L = (mid2 + side2) << shift;
60212 temp3L = (mid3 + side3) << shift;
60213 temp0R = (mid0 - side0) << shift;
60214 temp1R = (mid1 - side1) << shift;
60215 temp2R = (mid2 - side2) << shift;
60216 temp3R = (mid3 - side3) << shift;
60217 pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor;
60218 pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor;
60219 pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor;
60220 pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor;
60221 pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor;
60222 pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor;
60223 pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor;
60224 pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor;
60227 for (i = 0; i < frameCount4; ++i) {
60228 drflac_uint32 temp0L;
60229 drflac_uint32 temp1L;
60230 drflac_uint32 temp2L;
60231 drflac_uint32 temp3L;
60232 drflac_uint32 temp0R;
60233 drflac_uint32 temp1R;
60234 drflac_uint32 temp2R;
60235 drflac_uint32 temp3R;
60236 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
60237 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
60238 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
60239 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
60240 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
60241 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
60242 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
60243 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
60244 mid0 = (mid0 << 1) | (side0 & 0x01);
60245 mid1 = (mid1 << 1) | (side1 & 0x01);
60246 mid2 = (mid2 << 1) | (side2 & 0x01);
60247 mid3 = (mid3 << 1) | (side3 & 0x01);
60248 temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1);
60249 temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1);
60250 temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1);
60251 temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1);
60252 temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1);
60253 temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1);
60254 temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1);
60255 temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1);
60256 pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor;
60257 pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor;
60258 pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor;
60259 pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor;
60260 pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor;
60261 pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor;
60262 pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor;
60263 pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor;
60266 for (i = (frameCount4 << 2); i < frameCount; ++i) {
60267 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
60268 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
60269 mid = (mid << 1) | (side & 0x01);
60270 pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor;
60271 pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor;
60274 #if defined(DRFLAC_SUPPORT_SSE2)
60275 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
60278 drflac_uint64 frameCount4 = frameCount >> 2;
60279 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
60280 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
60281 drflac_uint32 shift = unusedBitsPerSample - 8;
60284 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
60285 factor = 1.0f / 8388608.0f;
60286 factor128 = _mm_set1_ps(factor);
60288 for (i = 0; i < frameCount4; ++i) {
60295 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
60296 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
60297 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
60298 tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
60299 tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
60300 leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
60301 rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
60302 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
60303 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
60305 for (i = (frameCount4 << 2); i < frameCount; ++i) {
60306 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
60307 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
60308 mid = (mid << 1) | (side & 0x01);
60309 pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor;
60310 pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor;
60314 for (i = 0; i < frameCount4; ++i) {
60321 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
60322 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
60323 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
60324 tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
60325 tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
60326 leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
60327 rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
60328 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
60329 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
60331 for (i = (frameCount4 << 2); i < frameCount; ++i) {
60332 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
60333 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
60334 mid = (mid << 1) | (side & 0x01);
60335 pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor;
60336 pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor;
60341 #if defined(DRFLAC_SUPPORT_NEON)
60342 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
60345 drflac_uint64 frameCount4 = frameCount >> 2;
60346 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
60347 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
60348 drflac_uint32 shift = unusedBitsPerSample - 8;
60350 float32x4_t factor4;
60354 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
60355 factor = 1.0f / 8388608.0f;
60356 factor4 = vdupq_n_f32(factor);
60357 wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
60358 wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
60360 for (i = 0; i < frameCount4; ++i) {
60364 float32x4_t rightf;
60365 uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4);
60366 uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4);
60367 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
60368 lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
60369 righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
60370 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
60371 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
60372 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
60374 for (i = (frameCount4 << 2); i < frameCount; ++i) {
60375 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
60376 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
60377 mid = (mid << 1) | (side & 0x01);
60378 pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor;
60379 pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor;
60383 shift4 = vdupq_n_s32(shift);
60384 for (i = 0; i < frameCount4; ++i) {
60390 float32x4_t rightf;
60391 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4);
60392 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4);
60393 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
60394 lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
60395 righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
60396 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
60397 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
60398 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
60400 for (i = (frameCount4 << 2); i < frameCount; ++i) {
60401 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
60402 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
60403 mid = (mid << 1) | (side & 0x01);
60404 pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor;
60405 pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor;
60410 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
60412 #if defined(DRFLAC_SUPPORT_SSE2)
60413 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
60414 drflac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
60416 #elif defined(DRFLAC_SUPPORT_NEON)
60417 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
60418 drflac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
60423 drflac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
60425 drflac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
60430 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
60432 for (drflac_uint64 i = 0; i < frameCount; ++i) {
60433 pOutputSamples[i*2+0] = (float)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0);
60434 pOutputSamples[i*2+1] = (float)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0);
60438 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
60441 drflac_uint64 frameCount4 = frameCount >> 2;
60442 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
60443 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
60444 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
60445 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
60446 float factor = 1 / 2147483648.0;
60447 for (i = 0; i < frameCount4; ++i) {
60448 drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
60449 drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
60450 drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
60451 drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
60452 drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
60453 drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
60454 drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
60455 drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
60456 pOutputSamples[i*8+0] = (drflac_int32)tempL0 * factor;
60457 pOutputSamples[i*8+1] = (drflac_int32)tempR0 * factor;
60458 pOutputSamples[i*8+2] = (drflac_int32)tempL1 * factor;
60459 pOutputSamples[i*8+3] = (drflac_int32)tempR1 * factor;
60460 pOutputSamples[i*8+4] = (drflac_int32)tempL2 * factor;
60461 pOutputSamples[i*8+5] = (drflac_int32)tempR2 * factor;
60462 pOutputSamples[i*8+6] = (drflac_int32)tempL3 * factor;
60463 pOutputSamples[i*8+7] = (drflac_int32)tempR3 * factor;
60465 for (i = (frameCount4 << 2); i < frameCount; ++i) {
60466 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor;
60467 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor;
60470 #if defined(DRFLAC_SUPPORT_SSE2)
60471 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
60474 drflac_uint64 frameCount4 = frameCount >> 2;
60475 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
60476 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
60477 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
60478 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
60479 float factor = 1.0f / 8388608.0f;
60480 __m128 factor128 = _mm_set1_ps(factor);
60481 for (i = 0; i < frameCount4; ++i) {
60486 lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
60487 righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
60488 leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128);
60489 rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128);
60490 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
60491 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
60493 for (i = (frameCount4 << 2); i < frameCount; ++i) {
60494 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor;
60495 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor;
60499 #if defined(DRFLAC_SUPPORT_NEON)
60500 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
60503 drflac_uint64 frameCount4 = frameCount >> 2;
60504 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
60505 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
60506 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
60507 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
60508 float factor = 1.0f / 8388608.0f;
60509 float32x4_t factor4 = vdupq_n_f32(factor);
60510 int32x4_t shift0_4 = vdupq_n_s32(shift0);
60511 int32x4_t shift1_4 = vdupq_n_s32(shift1);
60512 for (i = 0; i < frameCount4; ++i) {
60516 float32x4_t rightf;
60517 lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4));
60518 righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4));
60519 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
60520 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
60521 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
60523 for (i = (frameCount4 << 2); i < frameCount; ++i) {
60524 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor;
60525 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor;
60529 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
60531 #if defined(DRFLAC_SUPPORT_SSE2)
60532 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
60533 drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
60535 #elif defined(DRFLAC_SUPPORT_NEON)
60536 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
60537 drflac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
60542 drflac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
60544 drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
60548 DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut)
60550 drflac_uint64 framesRead;
60551 drflac_uint32 unusedBitsPerSample;
60552 if (pFlac == NULL || framesToRead == 0) {
60555 if (pBufferOut == NULL) {
60556 return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead);
60558 DRFLAC_ASSERT(pFlac->bitsPerSample <= 32);
60559 unusedBitsPerSample = 32 - pFlac->bitsPerSample;
60561 while (framesToRead > 0) {
60562 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
60563 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
60567 unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
60568 drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
60569 drflac_uint64 frameCountThisIteration = framesToRead;
60570 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
60571 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
60573 if (channelCount == 2) {
60574 const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
60575 const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
60576 switch (pFlac->currentFLACFrame.header.channelAssignment)
60578 case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
60580 drflac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
60582 case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
60584 drflac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
60586 case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
60588 drflac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
60590 case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
60593 drflac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
60598 for (i = 0; i < frameCountThisIteration; ++i) {
60600 for (j = 0; j < channelCount; ++j) {
60601 drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
60602 pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0);
60606 framesRead += frameCountThisIteration;
60607 pBufferOut += frameCountThisIteration * channelCount;
60608 framesToRead -= frameCountThisIteration;
60609 pFlac->currentPCMFrame += frameCountThisIteration;
60610 pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration;
60615 DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex)
60617 if (pFlac == NULL) {
60618 return DRFLAC_FALSE;
60620 if (pFlac->currentPCMFrame == pcmFrameIndex) {
60621 return DRFLAC_TRUE;
60623 if (pFlac->firstFLACFramePosInBytes == 0) {
60624 return DRFLAC_FALSE;
60626 if (pcmFrameIndex == 0) {
60627 pFlac->currentPCMFrame = 0;
60628 return drflac__seek_to_first_frame(pFlac);
60630 drflac_bool32 wasSuccessful = DRFLAC_FALSE;
60631 drflac_uint64 originalPCMFrame = pFlac->currentPCMFrame;
60632 if (pcmFrameIndex > pFlac->totalPCMFrameCount) {
60633 pcmFrameIndex = pFlac->totalPCMFrameCount;
60635 if (pcmFrameIndex > pFlac->currentPCMFrame) {
60636 drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex - pFlac->currentPCMFrame);
60637 if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) {
60638 pFlac->currentFLACFrame.pcmFramesRemaining -= offset;
60639 pFlac->currentPCMFrame = pcmFrameIndex;
60640 return DRFLAC_TRUE;
60643 drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentPCMFrame - pcmFrameIndex);
60644 drflac_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
60645 drflac_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining;
60646 if (currentFLACFramePCMFramesConsumed > offsetAbs) {
60647 pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs;
60648 pFlac->currentPCMFrame = pcmFrameIndex;
60649 return DRFLAC_TRUE;
60652 #ifndef DR_FLAC_NO_OGG
60653 if (pFlac->container == drflac_container_ogg)
60655 wasSuccessful = drflac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex);
60660 if (!pFlac->_noSeekTableSeek) {
60661 wasSuccessful = drflac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex);
60663 #if !defined(DR_FLAC_NO_CRC)
60664 if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) {
60665 wasSuccessful = drflac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex);
60668 if (!wasSuccessful && !pFlac->_noBruteForceSeek) {
60669 wasSuccessful = drflac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex);
60672 if (wasSuccessful) {
60673 pFlac->currentPCMFrame = pcmFrameIndex;
60675 if (drflac_seek_to_pcm_frame(pFlac, originalPCMFrame) == DRFLAC_FALSE) {
60676 drflac_seek_to_pcm_frame(pFlac, 0);
60679 return wasSuccessful;
60682 #if defined(SIZE_MAX)
60683 #define DRFLAC_SIZE_MAX SIZE_MAX
60685 #if defined(DRFLAC_64BIT)
60686 #define DRFLAC_SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF)
60688 #define DRFLAC_SIZE_MAX 0xFFFFFFFF
60691 #define DRFLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \
60692 static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)\
60694 type* pSampleData = NULL; \
60695 drflac_uint64 totalPCMFrameCount; \
60697 DRFLAC_ASSERT(pFlac != NULL); \
60699 totalPCMFrameCount = pFlac->totalPCMFrameCount; \
60701 if (totalPCMFrameCount == 0) { \
60702 type buffer[4096]; \
60703 drflac_uint64 pcmFramesRead; \
60704 size_t sampleDataBufferSize = sizeof(buffer); \
60706 pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \
60707 if (pSampleData == NULL) { \
60711 while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \
60712 if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \
60713 type* pNewSampleData; \
60714 size_t newSampleDataBufferSize; \
60716 newSampleDataBufferSize = sampleDataBufferSize * 2; \
60717 pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \
60718 if (pNewSampleData == NULL) { \
60719 drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \
60723 sampleDataBufferSize = newSampleDataBufferSize; \
60724 pSampleData = pNewSampleData; \
60727 DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \
60728 totalPCMFrameCount += pcmFramesRead; \
60732 DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \
60734 drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \
60735 if (dataSize > DRFLAC_SIZE_MAX) { \
60739 pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \
60740 if (pSampleData == NULL) { \
60744 totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \
60747 if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \
60748 if (channelsOut) *channelsOut = pFlac->channels; \
60749 if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \
60751 drflac_close(pFlac); \
60752 return pSampleData; \
60755 drflac_close(pFlac); \
60758 DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32)
60759 DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16)
60760 DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float)
60761 DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
60767 if (sampleRateOut) {
60768 *sampleRateOut = 0;
60770 if (totalPCMFrameCountOut) {
60771 *totalPCMFrameCountOut = 0;
60773 pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
60774 if (pFlac == NULL) {
60777 return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
60779 DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
60785 if (sampleRateOut) {
60786 *sampleRateOut = 0;
60788 if (totalPCMFrameCountOut) {
60789 *totalPCMFrameCountOut = 0;
60791 pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
60792 if (pFlac == NULL) {
60795 return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
60797 DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
60803 if (sampleRateOut) {
60804 *sampleRateOut = 0;
60806 if (totalPCMFrameCountOut) {
60807 *totalPCMFrameCountOut = 0;
60809 pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
60810 if (pFlac == NULL) {
60813 return drflac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
60815 #ifndef DR_FLAC_NO_STDIO
60816 DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
60825 if (totalPCMFrameCount) {
60826 *totalPCMFrameCount = 0;
60828 pFlac = drflac_open_file(filename, pAllocationCallbacks);
60829 if (pFlac == NULL) {
60832 return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
60834 DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
60843 if (totalPCMFrameCount) {
60844 *totalPCMFrameCount = 0;
60846 pFlac = drflac_open_file(filename, pAllocationCallbacks);
60847 if (pFlac == NULL) {
60850 return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
60852 DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
60861 if (totalPCMFrameCount) {
60862 *totalPCMFrameCount = 0;
60864 pFlac = drflac_open_file(filename, pAllocationCallbacks);
60865 if (pFlac == NULL) {
60868 return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
60871 DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
60880 if (totalPCMFrameCount) {
60881 *totalPCMFrameCount = 0;
60883 pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks);
60884 if (pFlac == NULL) {
60887 return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
60889 DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
60898 if (totalPCMFrameCount) {
60899 *totalPCMFrameCount = 0;
60901 pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks);
60902 if (pFlac == NULL) {
60905 return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
60907 DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
60916 if (totalPCMFrameCount) {
60917 *totalPCMFrameCount = 0;
60919 pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks);
60920 if (pFlac == NULL) {
60923 return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
60925 DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks)
60927 if (pAllocationCallbacks != NULL) {
60928 drflac__free_from_callbacks(p, pAllocationCallbacks);
60930 drflac__free_default(p, NULL);
60933 DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments)
60935 if (pIter == NULL) {
60938 pIter->countRemaining = commentCount;
60939 pIter->pRunningData = (const char*)pComments;
60941 DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut)
60943 drflac_int32 length;
60944 const char* pComment;
60945 if (pCommentLengthOut) {
60946 *pCommentLengthOut = 0;
60948 if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
60951 length = drflac__le2host_32(*(const drflac_uint32*)pIter->pRunningData);
60952 pIter->pRunningData += 4;
60953 pComment = pIter->pRunningData;
60954 pIter->pRunningData += length;
60955 pIter->countRemaining -= 1;
60956 if (pCommentLengthOut) {
60957 *pCommentLengthOut = length;
60961 DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData)
60963 if (pIter == NULL) {
60966 pIter->countRemaining = trackCount;
60967 pIter->pRunningData = (const char*)pTrackData;
60969 DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack)
60971 drflac_cuesheet_track cuesheetTrack;
60972 const char* pRunningData;
60973 drflac_uint64 offsetHi;
60974 drflac_uint64 offsetLo;
60975 if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
60976 return DRFLAC_FALSE;
60978 pRunningData = pIter->pRunningData;
60979 offsetHi = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
60980 offsetLo = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
60981 cuesheetTrack.offset = offsetLo | (offsetHi << 32);
60982 cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1;
60983 DRFLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12;
60984 cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0;
60985 cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14;
60986 cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1;
60987 cuesheetTrack.pIndexPoints = (const drflac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(drflac_cuesheet_track_index);
60988 pIter->pRunningData = pRunningData;
60989 pIter->countRemaining -= 1;
60990 if (pCuesheetTrack) {
60991 *pCuesheetTrack = cuesheetTrack;
60993 return DRFLAC_TRUE;
60995 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
60996 #pragma GCC diagnostic pop
60999 /* dr_flac_c end */
61000 #endif /* DRFLAC_IMPLEMENTATION */
61001 #endif /* MA_NO_FLAC */
61003 #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)
61004 #if !defined(DR_MP3_IMPLEMENTATION) && !defined(DRMP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
61005 /* dr_mp3_c begin */
61008 #include <stdlib.h>
61009 #include <string.h>
61010 #include <limits.h>
61011 DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision)
61014 *pMajor = DRMP3_VERSION_MAJOR;
61017 *pMinor = DRMP3_VERSION_MINOR;
61020 *pRevision = DRMP3_VERSION_REVISION;
61023 DRMP3_API const char* drmp3_version_string(void)
61025 return DRMP3_VERSION_STRING;
61027 #if defined(__TINYC__)
61028 #define DR_MP3_NO_SIMD
61030 #define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset)))
61031 #define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304
61032 #ifndef DRMP3_MAX_FRAME_SYNC_MATCHES
61033 #define DRMP3_MAX_FRAME_SYNC_MATCHES 10
61035 #define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE
61036 #define DRMP3_MAX_BITRESERVOIR_BYTES 511
61037 #define DRMP3_SHORT_BLOCK_TYPE 2
61038 #define DRMP3_STOP_BLOCK_TYPE 3
61039 #define DRMP3_MODE_MONO 3
61040 #define DRMP3_MODE_JOINT_STEREO 1
61041 #define DRMP3_HDR_SIZE 4
61042 #define DRMP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0)
61043 #define DRMP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60)
61044 #define DRMP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0)
61045 #define DRMP3_HDR_IS_CRC(h) (!((h[1]) & 1))
61046 #define DRMP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2)
61047 #define DRMP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8)
61048 #define DRMP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10)
61049 #define DRMP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10)
61050 #define DRMP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20)
61051 #define DRMP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3)
61052 #define DRMP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3)
61053 #define DRMP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3)
61054 #define DRMP3_HDR_GET_BITRATE(h) ((h[2]) >> 4)
61055 #define DRMP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3)
61056 #define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3)
61057 #define DRMP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2)
61058 #define DRMP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6)
61059 #define DRMP3_BITS_DEQUANTIZER_OUT -1
61060 #define DRMP3_MAX_SCF (255 + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210)
61061 #define DRMP3_MAX_SCFI ((DRMP3_MAX_SCF + 3) & ~3)
61062 #define DRMP3_MIN(a, b) ((a) > (b) ? (b) : (a))
61063 #define DRMP3_MAX(a, b) ((a) < (b) ? (b) : (a))
61064 #if !defined(DR_MP3_NO_SIMD)
61065 #if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64))
61066 #define DR_MP3_ONLY_SIMD
61068 #if ((defined(_MSC_VER) && _MSC_VER >= 1400) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__))
61069 #if defined(_MSC_VER)
61070 #include <intrin.h>
61072 #include <emmintrin.h>
61073 #define DRMP3_HAVE_SSE 1
61074 #define DRMP3_HAVE_SIMD 1
61075 #define DRMP3_VSTORE _mm_storeu_ps
61076 #define DRMP3_VLD _mm_loadu_ps
61077 #define DRMP3_VSET _mm_set1_ps
61078 #define DRMP3_VADD _mm_add_ps
61079 #define DRMP3_VSUB _mm_sub_ps
61080 #define DRMP3_VMUL _mm_mul_ps
61081 #define DRMP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y))
61082 #define DRMP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y))
61083 #define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s))
61084 #define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3))
61085 typedef __m128 drmp3_f4;
61086 #if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD)
61087 #define drmp3_cpuid __cpuid
61089 static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType)
61091 #if defined(__PIC__)
61092 __asm__ __volatile__(
61093 #if defined(__x86_64__)
61096 "xchgl %%ebx, %1\n"
61099 "xchgl %%ebx, %1\n"
61101 "xchgl %%ebx, %1\n"
61103 : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3])
61106 __asm__ __volatile__(
61108 : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3])
61113 static int drmp3_have_simd(void)
61115 #ifdef DR_MP3_ONLY_SIMD
61118 static int g_have_simd;
61120 #ifdef MINIMP3_TEST
61121 static int g_counter;
61122 if (g_counter++ > 100)
61127 drmp3_cpuid(CPUInfo, 0);
61128 if (CPUInfo[0] > 0)
61130 drmp3_cpuid(CPUInfo, 1);
61131 g_have_simd = (CPUInfo[3] & (1 << 26)) + 1;
61132 return g_have_simd - 1;
61135 return g_have_simd - 1;
61138 #elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)
61139 #include <arm_neon.h>
61140 #define DRMP3_HAVE_SSE 0
61141 #define DRMP3_HAVE_SIMD 1
61142 #define DRMP3_VSTORE vst1q_f32
61143 #define DRMP3_VLD vld1q_f32
61144 #define DRMP3_VSET vmovq_n_f32
61145 #define DRMP3_VADD vaddq_f32
61146 #define DRMP3_VSUB vsubq_f32
61147 #define DRMP3_VMUL vmulq_f32
61148 #define DRMP3_VMAC(a, x, y) vmlaq_f32(a, x, y)
61149 #define DRMP3_VMSB(a, x, y) vmlsq_f32(a, x, y)
61150 #define DRMP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s))
61151 #define DRMP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x)))
61152 typedef float32x4_t drmp3_f4;
61153 static int drmp3_have_simd(void)
61158 #define DRMP3_HAVE_SSE 0
61159 #define DRMP3_HAVE_SIMD 0
61160 #ifdef DR_MP3_ONLY_SIMD
61161 #error DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled
61165 #define DRMP3_HAVE_SIMD 0
61167 #if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64)
61168 #define DRMP3_HAVE_ARMV6 1
61169 static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(int32_t a)
61172 __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a));
61176 #define DRMP3_HAVE_ARMV6 0
61180 const drmp3_uint8 *buf;
61186 drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64];
61187 } drmp3_L12_scale_info;
61190 drmp3_uint8 tab_offset, code_tab_width, band_count;
61191 } drmp3_L12_subband_alloc;
61194 const drmp3_uint8 *sfbtab;
61195 drmp3_uint16 part_23_length, big_values, scalefac_compress;
61196 drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb;
61197 drmp3_uint8 table_select[3], region_count[3], subblock_gain[3];
61198 drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi;
61199 } drmp3_L3_gr_info;
61203 drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES];
61204 drmp3_L3_gr_info gr_info[4];
61205 float grbuf[2][576], scf[40], syn[18 + 15][2*32];
61206 drmp3_uint8 ist_pos[2][39];
61207 } drmp3dec_scratch;
61208 static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes)
61212 bs->limit = bytes*8;
61214 static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs *bs, int n)
61216 drmp3_uint32 next, cache = 0, s = bs->pos & 7;
61218 const drmp3_uint8 *p = bs->buf + (bs->pos >> 3);
61219 if ((bs->pos += n) > bs->limit)
61221 next = *p++ & (255 >> s);
61222 while ((shl -= 8) > 0)
61224 cache |= next << shl;
61227 return cache | (next >> -shl);
61229 static int drmp3_hdr_valid(const drmp3_uint8 *h)
61231 return h[0] == 0xff &&
61232 ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) &&
61233 (DRMP3_HDR_GET_LAYER(h) != 0) &&
61234 (DRMP3_HDR_GET_BITRATE(h) != 15) &&
61235 (DRMP3_HDR_GET_SAMPLE_RATE(h) != 3);
61237 static int drmp3_hdr_compare(const drmp3_uint8 *h1, const drmp3_uint8 *h2)
61239 return drmp3_hdr_valid(h2) &&
61240 ((h1[1] ^ h2[1]) & 0xFE) == 0 &&
61241 ((h1[2] ^ h2[2]) & 0x0C) == 0 &&
61242 !(DRMP3_HDR_IS_FREE_FORMAT(h1) ^ DRMP3_HDR_IS_FREE_FORMAT(h2));
61244 static unsigned drmp3_hdr_bitrate_kbps(const drmp3_uint8 *h)
61246 static const drmp3_uint8 halfrate[2][3][15] = {
61247 { { 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 } },
61248 { { 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 } },
61250 return 2*halfrate[!!DRMP3_HDR_TEST_MPEG1(h)][DRMP3_HDR_GET_LAYER(h) - 1][DRMP3_HDR_GET_BITRATE(h)];
61252 static unsigned drmp3_hdr_sample_rate_hz(const drmp3_uint8 *h)
61254 static const unsigned g_hz[3] = { 44100, 48000, 32000 };
61255 return g_hz[DRMP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!DRMP3_HDR_TEST_MPEG1(h) >> (int)!DRMP3_HDR_TEST_NOT_MPEG25(h);
61257 static unsigned drmp3_hdr_frame_samples(const drmp3_uint8 *h)
61259 return DRMP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)DRMP3_HDR_IS_FRAME_576(h));
61261 static int drmp3_hdr_frame_bytes(const drmp3_uint8 *h, int free_format_size)
61263 int frame_bytes = drmp3_hdr_frame_samples(h)*drmp3_hdr_bitrate_kbps(h)*125/drmp3_hdr_sample_rate_hz(h);
61264 if (DRMP3_HDR_IS_LAYER_1(h))
61268 return frame_bytes ? frame_bytes : free_format_size;
61270 static int drmp3_hdr_padding(const drmp3_uint8 *h)
61272 return DRMP3_HDR_TEST_PADDING(h) ? (DRMP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0;
61274 #ifndef DR_MP3_ONLY_MP3
61275 static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_uint8 *hdr, drmp3_L12_scale_info *sci)
61277 const drmp3_L12_subband_alloc *alloc;
61278 int mode = DRMP3_HDR_GET_STEREO_MODE(hdr);
61279 int nbands, stereo_bands = (mode == DRMP3_MODE_MONO) ? 0 : (mode == DRMP3_MODE_JOINT_STEREO) ? (DRMP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32;
61280 if (DRMP3_HDR_IS_LAYER_1(hdr))
61282 static const drmp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } };
61283 alloc = g_alloc_L1;
61285 } else if (!DRMP3_HDR_TEST_MPEG1(hdr))
61287 static const drmp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } };
61288 alloc = g_alloc_L2M2;
61292 static const drmp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } };
61293 int sample_rate_idx = DRMP3_HDR_GET_SAMPLE_RATE(hdr);
61294 unsigned kbps = drmp3_hdr_bitrate_kbps(hdr) >> (int)(mode != DRMP3_MODE_MONO);
61299 alloc = g_alloc_L2M1;
61303 static const drmp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } };
61304 alloc = g_alloc_L2M1_lowrate;
61305 nbands = sample_rate_idx == 2 ? 12 : 8;
61306 } else if (kbps >= 96 && sample_rate_idx != 1)
61311 sci->total_bands = (drmp3_uint8)nbands;
61312 sci->stereo_bands = (drmp3_uint8)DRMP3_MIN(stereo_bands, nbands);
61315 static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_uint8 *scfcod, int bands, float *scf)
61317 static const float g_deq_L12[18*3] = {
61318 #define DRMP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x
61319 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)
61322 for (i = 0; i < bands; i++)
61326 int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0;
61327 for (m = 4; m; m >>= 1)
61331 int b = drmp3_bs_get_bits(bs, 6);
61332 s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3);
61338 static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp3_L12_scale_info *sci)
61340 static const drmp3_uint8 g_bitalloc_code_tab[] = {
61341 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16,
61342 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16,
61343 0,17,18, 3,19,4,5,16,
61345 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15,
61346 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14,
61347 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16
61349 const drmp3_L12_subband_alloc *subband_alloc = drmp3_L12_subband_alloc_table(hdr, sci);
61350 int i, k = 0, ba_bits = 0;
61351 const drmp3_uint8 *ba_code_tab = g_bitalloc_code_tab;
61352 for (i = 0; i < sci->total_bands; i++)
61357 k += subband_alloc->band_count;
61358 ba_bits = subband_alloc->code_tab_width;
61359 ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset;
61362 ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)];
61363 sci->bitalloc[2*i] = ba;
61364 if (i < sci->stereo_bands)
61366 ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)];
61368 sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0;
61370 for (i = 0; i < 2*sci->total_bands; i++)
61372 sci->scfcod[i] = (drmp3_uint8)(sci->bitalloc[i] ? DRMP3_HDR_IS_LAYER_1(hdr) ? 2 : drmp3_bs_get_bits(bs, 2) : 6);
61374 drmp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf);
61375 for (i = sci->stereo_bands; i < sci->total_bands; i++)
61377 sci->bitalloc[2*i + 1] = 0;
61380 static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_scale_info *sci, int group_size)
61382 int i, j, k, choff = 576;
61383 for (j = 0; j < 4; j++)
61385 float *dst = grbuf + group_size*j;
61386 for (i = 0; i < 2*sci->total_bands; i++)
61388 int ba = sci->bitalloc[i];
61393 int half = (1 << (ba - 1)) - 1;
61394 for (k = 0; k < group_size; k++)
61396 dst[k] = (float)((int)drmp3_bs_get_bits(bs, ba) - half);
61400 unsigned mod = (2 << (ba - 17)) + 1;
61401 unsigned code = drmp3_bs_get_bits(bs, mod + 2 - (mod >> 3));
61402 for (k = 0; k < group_size; k++, code /= mod)
61404 dst[k] = (float)((int)(code % mod - mod/2));
61409 choff = 18 - choff;
61412 return group_size*4;
61414 static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, float *dst)
61417 memcpy(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float));
61418 for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6)
61420 for (k = 0; k < 12; k++)
61422 dst[k + 0] *= scf[0];
61423 dst[k + 576] *= scf[3];
61428 static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr)
61430 static const drmp3_uint8 g_scf_long[8][23] = {
61431 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
61432 { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 },
61433 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
61434 { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 },
61435 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
61436 { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 },
61437 { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 },
61438 { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 }
61440 static const drmp3_uint8 g_scf_short[8][40] = {
61441 { 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 },
61442 { 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 },
61443 { 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 },
61444 { 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 },
61445 { 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 },
61446 { 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 },
61447 { 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 },
61448 { 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 }
61450 static const drmp3_uint8 g_scf_mixed[8][40] = {
61451 { 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 },
61452 { 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 },
61453 { 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 },
61454 { 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 },
61455 { 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 },
61456 { 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 },
61457 { 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 },
61458 { 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 }
61460 unsigned tables, scfsi = 0;
61461 int main_data_begin, part_23_sum = 0;
61462 int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2;
61463 int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0);
61464 if (DRMP3_HDR_TEST_MPEG1(hdr))
61467 main_data_begin = drmp3_bs_get_bits(bs, 9);
61468 scfsi = drmp3_bs_get_bits(bs, 7 + gr_count);
61471 main_data_begin = drmp3_bs_get_bits(bs, 8 + gr_count) >> gr_count;
61475 if (DRMP3_HDR_IS_MONO(hdr))
61479 gr->part_23_length = (drmp3_uint16)drmp3_bs_get_bits(bs, 12);
61480 part_23_sum += gr->part_23_length;
61481 gr->big_values = (drmp3_uint16)drmp3_bs_get_bits(bs, 9);
61482 if (gr->big_values > 288)
61486 gr->global_gain = (drmp3_uint8)drmp3_bs_get_bits(bs, 8);
61487 gr->scalefac_compress = (drmp3_uint16)drmp3_bs_get_bits(bs, DRMP3_HDR_TEST_MPEG1(hdr) ? 4 : 9);
61488 gr->sfbtab = g_scf_long[sr_idx];
61489 gr->n_long_sfb = 22;
61490 gr->n_short_sfb = 0;
61491 if (drmp3_bs_get_bits(bs, 1))
61493 gr->block_type = (drmp3_uint8)drmp3_bs_get_bits(bs, 2);
61494 if (!gr->block_type)
61498 gr->mixed_block_flag = (drmp3_uint8)drmp3_bs_get_bits(bs, 1);
61499 gr->region_count[0] = 7;
61500 gr->region_count[1] = 255;
61501 if (gr->block_type == DRMP3_SHORT_BLOCK_TYPE)
61504 if (!gr->mixed_block_flag)
61506 gr->region_count[0] = 8;
61507 gr->sfbtab = g_scf_short[sr_idx];
61508 gr->n_long_sfb = 0;
61509 gr->n_short_sfb = 39;
61512 gr->sfbtab = g_scf_mixed[sr_idx];
61513 gr->n_long_sfb = DRMP3_HDR_TEST_MPEG1(hdr) ? 8 : 6;
61514 gr->n_short_sfb = 30;
61517 tables = drmp3_bs_get_bits(bs, 10);
61519 gr->subblock_gain[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
61520 gr->subblock_gain[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
61521 gr->subblock_gain[2] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
61524 gr->block_type = 0;
61525 gr->mixed_block_flag = 0;
61526 tables = drmp3_bs_get_bits(bs, 15);
61527 gr->region_count[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 4);
61528 gr->region_count[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
61529 gr->region_count[2] = 255;
61531 gr->table_select[0] = (drmp3_uint8)(tables >> 10);
61532 gr->table_select[1] = (drmp3_uint8)((tables >> 5) & 31);
61533 gr->table_select[2] = (drmp3_uint8)((tables) & 31);
61534 gr->preflag = (drmp3_uint8)(DRMP3_HDR_TEST_MPEG1(hdr) ? drmp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500));
61535 gr->scalefac_scale = (drmp3_uint8)drmp3_bs_get_bits(bs, 1);
61536 gr->count1_table = (drmp3_uint8)drmp3_bs_get_bits(bs, 1);
61537 gr->scfsi = (drmp3_uint8)((scfsi >> 12) & 15);
61540 } while(--gr_count);
61541 if (part_23_sum + bs->pos > bs->limit + main_data_begin*8)
61545 return main_data_begin;
61547 static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, const drmp3_uint8 *scf_size, const drmp3_uint8 *scf_count, drmp3_bs *bitbuf, int scfsi)
61550 for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2)
61552 int cnt = scf_count[i];
61555 memcpy(scf, ist_pos, cnt);
61558 int bits = scf_size[i];
61561 memset(scf, 0, cnt);
61562 memset(ist_pos, 0, cnt);
61565 int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1;
61566 for (k = 0; k < cnt; k++)
61568 int s = drmp3_bs_get_bits(bitbuf, bits);
61569 ist_pos[k] = (drmp3_uint8)(s == max_scf ? -1 : s);
61570 scf[k] = (drmp3_uint8)s;
61577 scf[0] = scf[1] = scf[2] = 0;
61579 static float drmp3_L3_ldexp_q2(float y, int exp_q2)
61581 static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f };
61585 e = DRMP3_MIN(30*4, exp_q2);
61586 y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2));
61587 } while ((exp_q2 -= e) > 0);
61590 static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch)
61592 static const drmp3_uint8 g_scf_partitions[3][28] = {
61593 { 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 },
61594 { 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 },
61595 { 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 }
61597 const drmp3_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb];
61598 drmp3_uint8 scf_size[4], iscf[40];
61599 int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi;
61601 if (DRMP3_HDR_TEST_MPEG1(hdr))
61603 static const drmp3_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 };
61604 int part = g_scfc_decode[gr->scalefac_compress];
61605 scf_size[1] = scf_size[0] = (drmp3_uint8)(part >> 2);
61606 scf_size[3] = scf_size[2] = (drmp3_uint8)(part & 3);
61609 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 };
61610 int k, modprod, sfc, ist = DRMP3_HDR_TEST_I_STEREO(hdr) && ch;
61611 sfc = gr->scalefac_compress >> ist;
61612 for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4)
61614 for (modprod = 1, i = 3; i >= 0; i--)
61616 scf_size[i] = (drmp3_uint8)(sfc / modprod % g_mod[k + i]);
61617 modprod *= g_mod[k + i];
61620 scf_partition += k;
61623 drmp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi);
61624 if (gr->n_short_sfb)
61626 int sh = 3 - scf_shift;
61627 for (i = 0; i < gr->n_short_sfb; i += 3)
61629 iscf[gr->n_long_sfb + i + 0] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh));
61630 iscf[gr->n_long_sfb + i + 1] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh));
61631 iscf[gr->n_long_sfb + i + 2] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh));
61633 } else if (gr->preflag)
61635 static const drmp3_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 };
61636 for (i = 0; i < 10; i++)
61638 iscf[11 + i] = (drmp3_uint8)(iscf[11 + i] + g_preamp[i]);
61641 gain_exp = gr->global_gain + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210 - (DRMP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0);
61642 gain = drmp3_L3_ldexp_q2(1 << (DRMP3_MAX_SCFI/4), DRMP3_MAX_SCFI - gain_exp);
61643 for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++)
61645 scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift);
61648 static const float g_drmp3_pow43[129 + 16] = {
61649 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,
61650 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
61652 static float drmp3_L3_pow_43(int x)
61655 int sign, mult = 256;
61658 return g_drmp3_pow43[16 + x];
61666 frac = (float)((x & 63) - sign) / ((x & ~63) + sign);
61667 return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult;
61669 static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit)
61671 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,
61672 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,
61673 -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,
61674 -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,
61675 -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,
61676 -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,
61677 -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,
61678 -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,
61679 -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,
61680 -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,
61681 -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,
61682 -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,
61683 -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,
61684 -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,
61685 -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,
61686 -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 };
61687 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};
61688 static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 };
61689 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 };
61690 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 };
61691 #define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - n))
61692 #define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); }
61693 #define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; }
61694 #define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh)
61696 int ireg = 0, big_val_cnt = gr_info->big_values;
61697 const drmp3_uint8 *sfb = gr_info->sfbtab;
61698 const drmp3_uint8 *bs_next_ptr = bs->buf + bs->pos/8;
61699 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);
61700 int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8;
61702 while (big_val_cnt > 0)
61704 int tab_num = gr_info->table_select[ireg];
61705 int sfb_cnt = gr_info->region_count[ireg++];
61706 const drmp3_int16 *codebook = tabs + tabindex[tab_num];
61707 int linbits = g_linbits[tab_num];
61713 pairs_to_decode = DRMP3_MIN(big_val_cnt, np);
61718 int leaf = codebook[DRMP3_PEEK_BITS(w)];
61721 DRMP3_FLUSH_BITS(w);
61723 leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)];
61725 DRMP3_FLUSH_BITS(leaf >> 8);
61726 for (j = 0; j < 2; j++, dst++, leaf >>= 4)
61728 int lsb = leaf & 0x0F;
61731 lsb += DRMP3_PEEK_BITS(linbits);
61732 DRMP3_FLUSH_BITS(linbits);
61734 *dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)bs_cache < 0 ? -1: 1);
61737 *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
61739 DRMP3_FLUSH_BITS(lsb ? 1 : 0);
61742 } while (--pairs_to_decode);
61743 } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
61749 pairs_to_decode = DRMP3_MIN(big_val_cnt, np);
61754 int leaf = codebook[DRMP3_PEEK_BITS(w)];
61757 DRMP3_FLUSH_BITS(w);
61759 leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)];
61761 DRMP3_FLUSH_BITS(leaf >> 8);
61762 for (j = 0; j < 2; j++, dst++, leaf >>= 4)
61764 int lsb = leaf & 0x0F;
61765 *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
61766 DRMP3_FLUSH_BITS(lsb ? 1 : 0);
61769 } while (--pairs_to_decode);
61770 } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
61773 for (np = 1 - big_val_cnt;; dst += 4)
61775 const drmp3_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32;
61776 int leaf = codebook_count1[DRMP3_PEEK_BITS(4)];
61779 leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))];
61781 DRMP3_FLUSH_BITS(leaf & 7);
61782 if (DRMP3_BSPOS > layer3gr_limit)
61786 #define DRMP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; }
61787 #define DRMP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((drmp3_int32)bs_cache < 0) ? -one : one; DRMP3_FLUSH_BITS(1) }
61788 DRMP3_RELOAD_SCALEFACTOR;
61789 DRMP3_DEQ_COUNT1(0);
61790 DRMP3_DEQ_COUNT1(1);
61791 DRMP3_RELOAD_SCALEFACTOR;
61792 DRMP3_DEQ_COUNT1(2);
61793 DRMP3_DEQ_COUNT1(3);
61796 bs->pos = layer3gr_limit;
61798 static void drmp3_L3_midside_stereo(float *left, int n)
61801 float *right = left + 576;
61802 #if DRMP3_HAVE_SIMD
61803 if (drmp3_have_simd()) for (; i < n - 3; i += 4)
61805 drmp3_f4 vl = DRMP3_VLD(left + i);
61806 drmp3_f4 vr = DRMP3_VLD(right + i);
61807 DRMP3_VSTORE(left + i, DRMP3_VADD(vl, vr));
61808 DRMP3_VSTORE(right + i, DRMP3_VSUB(vl, vr));
61814 float b = right[i];
61819 static void drmp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr)
61822 for (i = 0; i < n; i++)
61824 left[i + 576] = left[i]*kr;
61825 left[i] = left[i]*kl;
61828 static void drmp3_L3_stereo_top_band(const float *right, const drmp3_uint8 *sfb, int nbands, int max_band[3])
61831 max_band[0] = max_band[1] = max_band[2] = -1;
61832 for (i = 0; i < nbands; i++)
61834 for (k = 0; k < sfb[i]; k += 2)
61836 if (right[k] != 0 || right[k + 1] != 0)
61838 max_band[i % 3] = i;
61845 static void drmp3_L3_stereo_process(float *left, const drmp3_uint8 *ist_pos, const drmp3_uint8 *sfb, const drmp3_uint8 *hdr, int max_band[3], int mpeg2_sh)
61847 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 };
61848 unsigned i, max_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 7 : 64;
61849 for (i = 0; sfb[i]; i++)
61851 unsigned ipos = ist_pos[i];
61852 if ((int)i > max_band[i % 3] && ipos < max_pos)
61854 float kl, kr, s = DRMP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1;
61855 if (DRMP3_HDR_TEST_MPEG1(hdr))
61857 kl = g_pan[2*ipos];
61858 kr = g_pan[2*ipos + 1];
61862 kr = drmp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh);
61869 drmp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s);
61870 } else if (DRMP3_HDR_TEST_MS_STEREO(hdr))
61872 drmp3_L3_midside_stereo(left, sfb[i]);
61877 static void drmp3_L3_intensity_stereo(float *left, drmp3_uint8 *ist_pos, const drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr)
61879 int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb;
61880 int i, max_blocks = gr->n_short_sfb ? 3 : 1;
61881 drmp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band);
61882 if (gr->n_long_sfb)
61884 max_band[0] = max_band[1] = max_band[2] = DRMP3_MAX(DRMP3_MAX(max_band[0], max_band[1]), max_band[2]);
61886 for (i = 0; i < max_blocks; i++)
61888 int default_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 3 : 0;
61889 int itop = n_sfb - max_blocks + i;
61890 int prev = itop - max_blocks;
61891 ist_pos[itop] = (drmp3_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]);
61893 drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1);
61895 static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sfb)
61898 float *src = grbuf, *dst = scratch;
61899 for (;0 != (len = *sfb); sfb += 3, src += 2*len)
61901 for (i = 0; i < len; i++, src++)
61903 *dst++ = src[0*len];
61904 *dst++ = src[1*len];
61905 *dst++ = src[2*len];
61908 memcpy(grbuf, scratch, (dst - scratch)*sizeof(float));
61910 static void drmp3_L3_antialias(float *grbuf, int nbands)
61912 static const float g_aa[2][8] = {
61913 {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f},
61914 {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f}
61916 for (; nbands > 0; nbands--, grbuf += 18)
61919 #if DRMP3_HAVE_SIMD
61920 if (drmp3_have_simd()) for (; i < 8; i += 4)
61922 drmp3_f4 vu = DRMP3_VLD(grbuf + 18 + i);
61923 drmp3_f4 vd = DRMP3_VLD(grbuf + 14 - i);
61924 drmp3_f4 vc0 = DRMP3_VLD(g_aa[0] + i);
61925 drmp3_f4 vc1 = DRMP3_VLD(g_aa[1] + i);
61926 vd = DRMP3_VREV(vd);
61927 DRMP3_VSTORE(grbuf + 18 + i, DRMP3_VSUB(DRMP3_VMUL(vu, vc0), DRMP3_VMUL(vd, vc1)));
61928 vd = DRMP3_VADD(DRMP3_VMUL(vu, vc1), DRMP3_VMUL(vd, vc0));
61929 DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vd));
61932 #ifndef DR_MP3_ONLY_SIMD
61935 float u = grbuf[18 + i];
61936 float d = grbuf[17 - i];
61937 grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i];
61938 grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i];
61943 static void drmp3_L3_dct3_9(float *y)
61945 float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4;
61946 s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8];
61949 t4 = (s4 + s2)*0.93969262f;
61950 t2 = (s8 + s2)*0.76604444f;
61951 s6 = (s4 - s8)*0.17364818f;
61958 s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7];
61960 t0 = (s5 + s1)*0.98480775f;
61961 t4 = (s5 - s7)*0.34202014f;
61962 t2 = (s1 + s7)*0.64278761f;
61963 s1 = (s1 - s5 - s7)*0.86602540f;
61976 static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands)
61979 static const float g_twid9[18] = {
61980 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
61982 for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9)
61984 float co[9], si[9];
61987 for (i = 0; i < 4; i++)
61989 si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2];
61990 co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2];
61991 si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3];
61992 co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]);
61994 drmp3_L3_dct3_9(co);
61995 drmp3_L3_dct3_9(si);
62001 #if DRMP3_HAVE_SIMD
62002 if (drmp3_have_simd()) for (; i < 8; i += 4)
62004 drmp3_f4 vovl = DRMP3_VLD(overlap + i);
62005 drmp3_f4 vc = DRMP3_VLD(co + i);
62006 drmp3_f4 vs = DRMP3_VLD(si + i);
62007 drmp3_f4 vr0 = DRMP3_VLD(g_twid9 + i);
62008 drmp3_f4 vr1 = DRMP3_VLD(g_twid9 + 9 + i);
62009 drmp3_f4 vw0 = DRMP3_VLD(window + i);
62010 drmp3_f4 vw1 = DRMP3_VLD(window + 9 + i);
62011 drmp3_f4 vsum = DRMP3_VADD(DRMP3_VMUL(vc, vr1), DRMP3_VMUL(vs, vr0));
62012 DRMP3_VSTORE(overlap + i, DRMP3_VSUB(DRMP3_VMUL(vc, vr0), DRMP3_VMUL(vs, vr1)));
62013 DRMP3_VSTORE(grbuf + i, DRMP3_VSUB(DRMP3_VMUL(vovl, vw0), DRMP3_VMUL(vsum, vw1)));
62014 vsum = DRMP3_VADD(DRMP3_VMUL(vovl, vw1), DRMP3_VMUL(vsum, vw0));
62015 DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vsum));
62020 float ovl = overlap[i];
62021 float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i];
62022 overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i];
62023 grbuf[i] = ovl*window[0 + i] - sum*window[9 + i];
62024 grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i];
62028 static void drmp3_L3_idct3(float x0, float x1, float x2, float *dst)
62030 float m1 = x1*0.86602540f;
62031 float a1 = x0 - x2*0.5f;
62036 static void drmp3_L3_imdct12(float *x, float *dst, float *overlap)
62038 static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f };
62039 float co[3], si[3];
62041 drmp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co);
62042 drmp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si);
62044 for (i = 0; i < 3; i++)
62046 float ovl = overlap[i];
62047 float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i];
62048 overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i];
62049 dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i];
62050 dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i];
62053 static void drmp3_L3_imdct_short(float *grbuf, float *overlap, int nbands)
62055 for (;nbands > 0; nbands--, overlap += 9, grbuf += 18)
62058 memcpy(tmp, grbuf, sizeof(tmp));
62059 memcpy(grbuf, overlap, 6*sizeof(float));
62060 drmp3_L3_imdct12(tmp, grbuf + 6, overlap + 6);
62061 drmp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6);
62062 drmp3_L3_imdct12(tmp + 2, overlap, overlap + 6);
62065 static void drmp3_L3_change_sign(float *grbuf)
62068 for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36)
62069 for (i = 1; i < 18; i += 2)
62070 grbuf[i] = -grbuf[i];
62072 static void drmp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands)
62074 static const float g_mdct_window[2][18] = {
62075 { 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 },
62076 { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f }
62080 drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands);
62081 grbuf += 18*n_long_bands;
62082 overlap += 9*n_long_bands;
62084 if (block_type == DRMP3_SHORT_BLOCK_TYPE)
62085 drmp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands);
62087 drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == DRMP3_STOP_BLOCK_TYPE], 32 - n_long_bands);
62089 static void drmp3_L3_save_reservoir(drmp3dec *h, drmp3dec_scratch *s)
62091 int pos = (s->bs.pos + 7)/8u;
62092 int remains = s->bs.limit/8u - pos;
62093 if (remains > DRMP3_MAX_BITRESERVOIR_BYTES)
62095 pos += remains - DRMP3_MAX_BITRESERVOIR_BYTES;
62096 remains = DRMP3_MAX_BITRESERVOIR_BYTES;
62100 memmove(h->reserv_buf, s->maindata + pos, remains);
62102 h->reserv = remains;
62104 static int drmp3_L3_restore_reservoir(drmp3dec *h, drmp3_bs *bs, drmp3dec_scratch *s, int main_data_begin)
62106 int frame_bytes = (bs->limit - bs->pos)/8;
62107 int bytes_have = DRMP3_MIN(h->reserv, main_data_begin);
62108 memcpy(s->maindata, h->reserv_buf + DRMP3_MAX(0, h->reserv - main_data_begin), DRMP3_MIN(h->reserv, main_data_begin));
62109 memcpy(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes);
62110 drmp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes);
62111 return h->reserv >= main_data_begin;
62113 static void drmp3_L3_decode(drmp3dec *h, drmp3dec_scratch *s, drmp3_L3_gr_info *gr_info, int nch)
62116 for (ch = 0; ch < nch; ch++)
62118 int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length;
62119 drmp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch);
62120 drmp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit);
62122 if (DRMP3_HDR_TEST_I_STEREO(h->header))
62124 drmp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header);
62125 } else if (DRMP3_HDR_IS_MS_STEREO(h->header))
62127 drmp3_L3_midside_stereo(s->grbuf[0], 576);
62129 for (ch = 0; ch < nch; ch++, gr_info++)
62132 int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(DRMP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2);
62133 if (gr_info->n_short_sfb)
62135 aa_bands = n_long_bands - 1;
62136 drmp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb);
62138 drmp3_L3_antialias(s->grbuf[ch], aa_bands);
62139 drmp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands);
62140 drmp3_L3_change_sign(s->grbuf[ch]);
62143 static void drmp3d_DCT_II(float *grbuf, int n)
62145 static const float g_sec[24] = {
62146 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
62149 #if DRMP3_HAVE_SIMD
62150 if (drmp3_have_simd()) for (; k < n; k += 4)
62152 drmp3_f4 t[4][8], *x;
62153 float *y = grbuf + k;
62154 for (x = t[0], i = 0; i < 8; i++, x++)
62156 drmp3_f4 x0 = DRMP3_VLD(&y[i*18]);
62157 drmp3_f4 x1 = DRMP3_VLD(&y[(15 - i)*18]);
62158 drmp3_f4 x2 = DRMP3_VLD(&y[(16 + i)*18]);
62159 drmp3_f4 x3 = DRMP3_VLD(&y[(31 - i)*18]);
62160 drmp3_f4 t0 = DRMP3_VADD(x0, x3);
62161 drmp3_f4 t1 = DRMP3_VADD(x1, x2);
62162 drmp3_f4 t2 = DRMP3_VMUL_S(DRMP3_VSUB(x1, x2), g_sec[3*i + 0]);
62163 drmp3_f4 t3 = DRMP3_VMUL_S(DRMP3_VSUB(x0, x3), g_sec[3*i + 1]);
62164 x[0] = DRMP3_VADD(t0, t1);
62165 x[8] = DRMP3_VMUL_S(DRMP3_VSUB(t0, t1), g_sec[3*i + 2]);
62166 x[16] = DRMP3_VADD(t3, t2);
62167 x[24] = DRMP3_VMUL_S(DRMP3_VSUB(t3, t2), g_sec[3*i + 2]);
62169 for (x = t[0], i = 0; i < 4; i++, x += 8)
62171 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;
62172 xt = DRMP3_VSUB(x0, x7); x0 = DRMP3_VADD(x0, x7);
62173 x7 = DRMP3_VSUB(x1, x6); x1 = DRMP3_VADD(x1, x6);
62174 x6 = DRMP3_VSUB(x2, x5); x2 = DRMP3_VADD(x2, x5);
62175 x5 = DRMP3_VSUB(x3, x4); x3 = DRMP3_VADD(x3, x4);
62176 x4 = DRMP3_VSUB(x0, x3); x0 = DRMP3_VADD(x0, x3);
62177 x3 = DRMP3_VSUB(x1, x2); x1 = DRMP3_VADD(x1, x2);
62178 x[0] = DRMP3_VADD(x0, x1);
62179 x[4] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x1), 0.70710677f);
62180 x5 = DRMP3_VADD(x5, x6);
62181 x6 = DRMP3_VMUL_S(DRMP3_VADD(x6, x7), 0.70710677f);
62182 x7 = DRMP3_VADD(x7, xt);
62183 x3 = DRMP3_VMUL_S(DRMP3_VADD(x3, x4), 0.70710677f);
62184 x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f));
62185 x7 = DRMP3_VADD(x7, DRMP3_VMUL_S(x5, 0.382683432f));
62186 x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f));
62187 x0 = DRMP3_VSUB(xt, x6); xt = DRMP3_VADD(xt, x6);
62188 x[1] = DRMP3_VMUL_S(DRMP3_VADD(xt, x7), 0.50979561f);
62189 x[2] = DRMP3_VMUL_S(DRMP3_VADD(x4, x3), 0.54119611f);
62190 x[3] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x5), 0.60134488f);
62191 x[5] = DRMP3_VMUL_S(DRMP3_VADD(x0, x5), 0.89997619f);
62192 x[6] = DRMP3_VMUL_S(DRMP3_VSUB(x4, x3), 1.30656302f);
62193 x[7] = DRMP3_VMUL_S(DRMP3_VSUB(xt, x7), 2.56291556f);
62198 #define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v)
62200 #define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[i*18], vget_low_f32(v))
62202 for (i = 0; i < 7; i++, y += 4*18)
62204 drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]);
62205 DRMP3_VSAVE2(0, t[0][i]);
62206 DRMP3_VSAVE2(1, DRMP3_VADD(t[2][i], s));
62207 DRMP3_VSAVE2(2, DRMP3_VADD(t[1][i], t[1][i + 1]));
62208 DRMP3_VSAVE2(3, DRMP3_VADD(t[2][1 + i], s));
62210 DRMP3_VSAVE2(0, t[0][7]);
62211 DRMP3_VSAVE2(1, DRMP3_VADD(t[2][7], t[3][7]));
62212 DRMP3_VSAVE2(2, t[1][7]);
62213 DRMP3_VSAVE2(3, t[3][7]);
62216 #define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[i*18], v)
62217 for (i = 0; i < 7; i++, y += 4*18)
62219 drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]);
62220 DRMP3_VSAVE4(0, t[0][i]);
62221 DRMP3_VSAVE4(1, DRMP3_VADD(t[2][i], s));
62222 DRMP3_VSAVE4(2, DRMP3_VADD(t[1][i], t[1][i + 1]));
62223 DRMP3_VSAVE4(3, DRMP3_VADD(t[2][1 + i], s));
62225 DRMP3_VSAVE4(0, t[0][7]);
62226 DRMP3_VSAVE4(1, DRMP3_VADD(t[2][7], t[3][7]));
62227 DRMP3_VSAVE4(2, t[1][7]);
62228 DRMP3_VSAVE4(3, t[3][7]);
62232 #ifdef DR_MP3_ONLY_SIMD
62237 float t[4][8], *x, *y = grbuf + k;
62238 for (x = t[0], i = 0; i < 8; i++, x++)
62240 float x0 = y[i*18];
62241 float x1 = y[(15 - i)*18];
62242 float x2 = y[(16 + i)*18];
62243 float x3 = y[(31 - i)*18];
62244 float t0 = x0 + x3;
62245 float t1 = x1 + x2;
62246 float t2 = (x1 - x2)*g_sec[3*i + 0];
62247 float t3 = (x0 - x3)*g_sec[3*i + 1];
62249 x[8] = (t0 - t1)*g_sec[3*i + 2];
62251 x[24] = (t3 - t2)*g_sec[3*i + 2];
62253 for (x = t[0], i = 0; i < 4; i++, x += 8)
62255 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;
62256 xt = x0 - x7; x0 += x7;
62257 x7 = x1 - x6; x1 += x6;
62258 x6 = x2 - x5; x2 += x5;
62259 x5 = x3 - x4; x3 += x4;
62260 x4 = x0 - x3; x0 += x3;
62261 x3 = x1 - x2; x1 += x2;
62263 x[4] = (x0 - x1)*0.70710677f;
62265 x6 = (x6 + x7)*0.70710677f;
62267 x3 = (x3 + x4)*0.70710677f;
62268 x5 -= x7*0.198912367f;
62269 x7 += x5*0.382683432f;
62270 x5 -= x7*0.198912367f;
62271 x0 = xt - x6; xt += x6;
62272 x[1] = (xt + x7)*0.50979561f;
62273 x[2] = (x4 + x3)*0.54119611f;
62274 x[3] = (x0 - x5)*0.60134488f;
62275 x[5] = (x0 + x5)*0.89997619f;
62276 x[6] = (x4 - x3)*1.30656302f;
62277 x[7] = (xt - x7)*2.56291556f;
62279 for (i = 0; i < 7; i++, y += 4*18)
62282 y[1*18] = t[2][i] + t[3][i] + t[3][i + 1];
62283 y[2*18] = t[1][i] + t[1][i + 1];
62284 y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1];
62287 y[1*18] = t[2][7] + t[3][7];
62293 #ifndef DR_MP3_FLOAT_OUTPUT
62294 typedef drmp3_int16 drmp3d_sample_t;
62295 static drmp3_int16 drmp3d_scale_pcm(float sample)
62298 #if DRMP3_HAVE_ARMV6
62299 drmp3_int32 s32 = (drmp3_int32)(sample + .5f);
62301 s = (drmp3_int16)drmp3_clip_int16_arm(s32);
62303 if (sample >= 32766.5) return (drmp3_int16) 32767;
62304 if (sample <= -32767.5) return (drmp3_int16)-32768;
62305 s = (drmp3_int16)(sample + .5f);
62311 typedef float drmp3d_sample_t;
62312 static float drmp3d_scale_pcm(float sample)
62314 return sample*(1.f/32768.f);
62317 static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z)
62320 a = (z[14*64] - z[ 0]) * 29;
62321 a += (z[ 1*64] + z[13*64]) * 213;
62322 a += (z[12*64] - z[ 2*64]) * 459;
62323 a += (z[ 3*64] + z[11*64]) * 2037;
62324 a += (z[10*64] - z[ 4*64]) * 5153;
62325 a += (z[ 5*64] + z[ 9*64]) * 6574;
62326 a += (z[ 8*64] - z[ 6*64]) * 37489;
62327 a += z[ 7*64] * 75038;
62328 pcm[0] = drmp3d_scale_pcm(a);
62330 a = z[14*64] * 104;
62331 a += z[12*64] * 1567;
62332 a += z[10*64] * 9727;
62333 a += z[ 8*64] * 64019;
62334 a += z[ 6*64] * -9975;
62335 a += z[ 4*64] * -45;
62336 a += z[ 2*64] * 146;
62337 a += z[ 0*64] * -5;
62338 pcm[16*nch] = drmp3d_scale_pcm(a);
62340 static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins)
62343 float *xr = xl + 576*(nch - 1);
62344 drmp3d_sample_t *dstr = dstl + (nch - 1);
62345 static const float g_win[] = {
62346 -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992,
62347 -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856,
62348 -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630,
62349 -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313,
62350 -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908,
62351 -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415,
62352 -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835,
62353 -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169,
62354 -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420,
62355 -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590,
62356 -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679,
62357 -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692,
62358 -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629,
62359 -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494,
62360 -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290
62362 float *zlin = lins + 15*64;
62363 const float *w = g_win;
62364 zlin[4*15] = xl[18*16];
62365 zlin[4*15 + 1] = xr[18*16];
62366 zlin[4*15 + 2] = xl[0];
62367 zlin[4*15 + 3] = xr[0];
62368 zlin[4*31] = xl[1 + 18*16];
62369 zlin[4*31 + 1] = xr[1 + 18*16];
62370 zlin[4*31 + 2] = xl[1];
62371 zlin[4*31 + 3] = xr[1];
62372 drmp3d_synth_pair(dstr, nch, lins + 4*15 + 1);
62373 drmp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1);
62374 drmp3d_synth_pair(dstl, nch, lins + 4*15);
62375 drmp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64);
62376 #if DRMP3_HAVE_SIMD
62377 if (drmp3_have_simd()) for (i = 14; i >= 0; i--)
62379 #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)]);
62380 #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)); }
62381 #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))); }
62382 #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))); }
62384 zlin[4*i] = xl[18*(31 - i)];
62385 zlin[4*i + 1] = xr[18*(31 - i)];
62386 zlin[4*i + 2] = xl[1 + 18*(31 - i)];
62387 zlin[4*i + 3] = xr[1 + 18*(31 - i)];
62388 zlin[4*i + 64] = xl[1 + 18*(1 + i)];
62389 zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)];
62390 zlin[4*i - 64 + 2] = xl[18*(1 + i)];
62391 zlin[4*i - 64 + 3] = xr[18*(1 + i)];
62392 DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7)
62394 #ifndef DR_MP3_FLOAT_OUTPUT
62396 static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
62397 static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
62398 __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
62399 _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
62400 dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1);
62401 dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5);
62402 dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0);
62403 dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4);
62404 dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3);
62405 dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7);
62406 dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2);
62407 dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6);
62409 int16x4_t pcma, pcmb;
62410 a = DRMP3_VADD(a, DRMP3_VSET(0.5f));
62411 b = DRMP3_VADD(b, DRMP3_VSET(0.5f));
62412 pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0)))));
62413 pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0)))));
62414 vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1);
62415 vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1);
62416 vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0);
62417 vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0);
62418 vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3);
62419 vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3);
62420 vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2);
62421 vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2);
62424 static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f };
62425 a = DRMP3_VMUL(a, g_scale);
62426 b = DRMP3_VMUL(b, g_scale);
62428 _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)));
62429 _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1)));
62430 _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)));
62431 _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0)));
62432 _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)));
62433 _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3)));
62434 _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)));
62435 _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2)));
62437 vst1q_lane_f32(dstr + (15 - i)*nch, a, 1);
62438 vst1q_lane_f32(dstr + (17 + i)*nch, b, 1);
62439 vst1q_lane_f32(dstl + (15 - i)*nch, a, 0);
62440 vst1q_lane_f32(dstl + (17 + i)*nch, b, 0);
62441 vst1q_lane_f32(dstr + (47 - i)*nch, a, 3);
62442 vst1q_lane_f32(dstr + (49 + i)*nch, b, 3);
62443 vst1q_lane_f32(dstl + (47 - i)*nch, a, 2);
62444 vst1q_lane_f32(dstl + (49 + i)*nch, b, 2);
62450 #ifdef DR_MP3_ONLY_SIMD
62453 for (i = 14; i >= 0; i--)
62455 #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];
62456 #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; }
62457 #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; }
62458 #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; }
62460 zlin[4*i] = xl[18*(31 - i)];
62461 zlin[4*i + 1] = xr[18*(31 - i)];
62462 zlin[4*i + 2] = xl[1 + 18*(31 - i)];
62463 zlin[4*i + 3] = xr[1 + 18*(31 - i)];
62464 zlin[4*(i + 16)] = xl[1 + 18*(1 + i)];
62465 zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)];
62466 zlin[4*(i - 16) + 2] = xl[18*(1 + i)];
62467 zlin[4*(i - 16) + 3] = xr[18*(1 + i)];
62468 DRMP3_S0(0) DRMP3_S2(1) DRMP3_S1(2) DRMP3_S2(3) DRMP3_S1(4) DRMP3_S2(5) DRMP3_S1(6) DRMP3_S2(7)
62469 dstr[(15 - i)*nch] = drmp3d_scale_pcm(a[1]);
62470 dstr[(17 + i)*nch] = drmp3d_scale_pcm(b[1]);
62471 dstl[(15 - i)*nch] = drmp3d_scale_pcm(a[0]);
62472 dstl[(17 + i)*nch] = drmp3d_scale_pcm(b[0]);
62473 dstr[(47 - i)*nch] = drmp3d_scale_pcm(a[3]);
62474 dstr[(49 + i)*nch] = drmp3d_scale_pcm(b[3]);
62475 dstl[(47 - i)*nch] = drmp3d_scale_pcm(a[2]);
62476 dstl[(49 + i)*nch] = drmp3d_scale_pcm(b[2]);
62480 static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, drmp3d_sample_t *pcm, float *lins)
62483 for (i = 0; i < nch; i++)
62485 drmp3d_DCT_II(grbuf + 576*i, nbands);
62487 memcpy(lins, qmf_state, sizeof(float)*15*64);
62488 for (i = 0; i < nbands; i += 2)
62490 drmp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64);
62492 #ifndef DR_MP3_NONSTANDARD_BUT_LOGICAL
62495 for (i = 0; i < 15*64; i += 2)
62497 qmf_state[i] = lins[nbands*64 + i];
62502 memcpy(qmf_state, lins + nbands*64, sizeof(float)*15*64);
62505 static int drmp3d_match_frame(const drmp3_uint8 *hdr, int mp3_bytes, int frame_bytes)
62508 for (i = 0, nmatch = 0; nmatch < DRMP3_MAX_FRAME_SYNC_MATCHES; nmatch++)
62510 i += drmp3_hdr_frame_bytes(hdr + i, frame_bytes) + drmp3_hdr_padding(hdr + i);
62511 if (i + DRMP3_HDR_SIZE > mp3_bytes)
62513 if (!drmp3_hdr_compare(hdr, hdr + i))
62518 static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes)
62521 for (i = 0; i < mp3_bytes - DRMP3_HDR_SIZE; i++, mp3++)
62523 if (drmp3_hdr_valid(mp3))
62525 int frame_bytes = drmp3_hdr_frame_bytes(mp3, *free_format_bytes);
62526 int frame_and_padding = frame_bytes + drmp3_hdr_padding(mp3);
62527 for (k = DRMP3_HDR_SIZE; !frame_bytes && k < DRMP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - DRMP3_HDR_SIZE; k++)
62529 if (drmp3_hdr_compare(mp3, mp3 + k))
62531 int fb = k - drmp3_hdr_padding(mp3);
62532 int nextfb = fb + drmp3_hdr_padding(mp3 + k);
62533 if (i + k + nextfb + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + k + nextfb))
62535 frame_and_padding = k;
62537 *free_format_bytes = fb;
62540 if ((frame_bytes && i + frame_and_padding <= mp3_bytes &&
62541 drmp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) ||
62542 (!i && frame_and_padding == mp3_bytes))
62544 *ptr_frame_bytes = frame_and_padding;
62547 *free_format_bytes = 0;
62550 *ptr_frame_bytes = 0;
62553 DRMP3_API void drmp3dec_init(drmp3dec *dec)
62555 dec->header[0] = 0;
62557 DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info)
62559 int i = 0, igr, frame_size = 0, success = 1;
62560 const drmp3_uint8 *hdr;
62561 drmp3_bs bs_frame[1];
62562 drmp3dec_scratch scratch;
62563 if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3))
62565 frame_size = drmp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + drmp3_hdr_padding(mp3);
62566 if (frame_size != mp3_bytes && (frame_size + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + frame_size)))
62573 memset(dec, 0, sizeof(drmp3dec));
62574 i = drmp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size);
62575 if (!frame_size || i + frame_size > mp3_bytes)
62577 info->frame_bytes = i;
62582 memcpy(dec->header, hdr, DRMP3_HDR_SIZE);
62583 info->frame_bytes = i + frame_size;
62584 info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2;
62585 info->hz = drmp3_hdr_sample_rate_hz(hdr);
62586 info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr);
62587 info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr);
62588 drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE);
62589 if (DRMP3_HDR_IS_CRC(hdr))
62591 drmp3_bs_get_bits(bs_frame, 16);
62593 if (info->layer == 3)
62595 int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr);
62596 if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit)
62598 drmp3dec_init(dec);
62601 success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin);
62602 if (success && pcm != NULL)
62604 for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels))
62606 memset(scratch.grbuf[0], 0, 576*2*sizeof(float));
62607 drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels);
62608 drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]);
62611 drmp3_L3_save_reservoir(dec, &scratch);
62614 #ifdef DR_MP3_ONLY_MP3
62617 drmp3_L12_scale_info sci[1];
62619 return drmp3_hdr_frame_samples(hdr);
62621 drmp3_L12_read_scale_info(hdr, bs_frame, sci);
62622 memset(scratch.grbuf[0], 0, 576*2*sizeof(float));
62623 for (i = 0, igr = 0; igr < 3; igr++)
62625 if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1)))
62628 drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]);
62629 drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]);
62630 memset(scratch.grbuf[0], 0, 576*2*sizeof(float));
62631 pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels);
62633 if (bs_frame->pos > bs_frame->limit)
62635 drmp3dec_init(dec);
62641 return success*drmp3_hdr_frame_samples(dec->header);
62643 DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples)
62646 #if DRMP3_HAVE_SIMD
62647 size_t aligned_count = num_samples & ~7;
62648 for(; i < aligned_count; i+=8)
62650 drmp3_f4 scale = DRMP3_VSET(32768.0f);
62651 drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), scale);
62652 drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), scale);
62654 drmp3_f4 s16max = DRMP3_VSET( 32767.0f);
62655 drmp3_f4 s16min = DRMP3_VSET(-32768.0f);
62656 __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)),
62657 _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min)));
62658 out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0);
62659 out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1);
62660 out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2);
62661 out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3);
62662 out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4);
62663 out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5);
62664 out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6);
62665 out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7);
62667 int16x4_t pcma, pcmb;
62668 a = DRMP3_VADD(a, DRMP3_VSET(0.5f));
62669 b = DRMP3_VADD(b, DRMP3_VSET(0.5f));
62670 pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0)))));
62671 pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0)))));
62672 vst1_lane_s16(out+i , pcma, 0);
62673 vst1_lane_s16(out+i+1, pcma, 1);
62674 vst1_lane_s16(out+i+2, pcma, 2);
62675 vst1_lane_s16(out+i+3, pcma, 3);
62676 vst1_lane_s16(out+i+4, pcmb, 0);
62677 vst1_lane_s16(out+i+5, pcmb, 1);
62678 vst1_lane_s16(out+i+6, pcmb, 2);
62679 vst1_lane_s16(out+i+7, pcmb, 3);
62683 for(; i < num_samples; i++)
62685 float sample = in[i] * 32768.0f;
62686 if (sample >= 32766.5)
62687 out[i] = (drmp3_int16) 32767;
62688 else if (sample <= -32767.5)
62689 out[i] = (drmp3_int16)-32768;
62692 short s = (drmp3_int16)(sample + .5f);
62699 #if defined(SIZE_MAX)
62700 #define DRMP3_SIZE_MAX SIZE_MAX
62702 #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
62703 #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF)
62705 #define DRMP3_SIZE_MAX 0xFFFFFFFF
62708 #ifndef DRMP3_SEEK_LEADING_MP3_FRAMES
62709 #define DRMP3_SEEK_LEADING_MP3_FRAMES 2
62711 #define DRMP3_MIN_DATA_CHUNK_SIZE 16384
62712 #ifndef DRMP3_DATA_CHUNK_SIZE
62713 #define DRMP3_DATA_CHUNK_SIZE DRMP3_MIN_DATA_CHUNK_SIZE*4
62715 #ifndef DRMP3_ASSERT
62716 #include <assert.h>
62717 #define DRMP3_ASSERT(expression) assert(expression)
62719 #ifndef DRMP3_COPY_MEMORY
62720 #define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
62722 #ifndef DRMP3_ZERO_MEMORY
62723 #define DRMP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
62725 #define DRMP3_ZERO_OBJECT(p) DRMP3_ZERO_MEMORY((p), sizeof(*(p)))
62726 #ifndef DRMP3_MALLOC
62727 #define DRMP3_MALLOC(sz) malloc((sz))
62729 #ifndef DRMP3_REALLOC
62730 #define DRMP3_REALLOC(p, sz) realloc((p), (sz))
62733 #define DRMP3_FREE(p) free((p))
62735 #define DRMP3_COUNTOF(x) (sizeof(x) / sizeof(x[0]))
62736 #define DRMP3_CLAMP(x, lo, hi) (DRMP3_MAX(lo, DRMP3_MIN(x, hi)))
62738 #define DRMP3_PI_D 3.14159265358979323846264
62740 #define DRMP3_DEFAULT_RESAMPLER_LPF_ORDER 2
62741 static DRMP3_INLINE float drmp3_mix_f32(float x, float y, float a)
62743 return x*(1-a) + y*a;
62745 static DRMP3_INLINE float drmp3_mix_f32_fast(float x, float y, float a)
62747 float r0 = (y - x);
62751 static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b)
62757 drmp3_uint32 t = a;
62764 static DRMP3_INLINE double drmp3_sin(double x)
62768 static DRMP3_INLINE double drmp3_exp(double x)
62772 static DRMP3_INLINE double drmp3_cos(double x)
62774 return drmp3_sin((DRMP3_PI_D*0.5) - x);
62776 static void* drmp3__malloc_default(size_t sz, void* pUserData)
62779 return DRMP3_MALLOC(sz);
62781 static void* drmp3__realloc_default(void* p, size_t sz, void* pUserData)
62784 return DRMP3_REALLOC(p, sz);
62786 static void drmp3__free_default(void* p, void* pUserData)
62791 static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks)
62793 if (pAllocationCallbacks == NULL) {
62796 if (pAllocationCallbacks->onMalloc != NULL) {
62797 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
62799 if (pAllocationCallbacks->onRealloc != NULL) {
62800 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
62804 static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drmp3_allocation_callbacks* pAllocationCallbacks)
62806 if (pAllocationCallbacks == NULL) {
62809 if (pAllocationCallbacks->onRealloc != NULL) {
62810 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
62812 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
62814 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
62819 DRMP3_COPY_MEMORY(p2, p, szOld);
62820 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
62826 static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks)
62828 if (p == NULL || pAllocationCallbacks == NULL) {
62831 if (pAllocationCallbacks->onFree != NULL) {
62832 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
62835 static drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drmp3_allocation_callbacks* pAllocationCallbacks)
62837 if (pAllocationCallbacks != NULL) {
62838 return *pAllocationCallbacks;
62840 drmp3_allocation_callbacks allocationCallbacks;
62841 allocationCallbacks.pUserData = NULL;
62842 allocationCallbacks.onMalloc = drmp3__malloc_default;
62843 allocationCallbacks.onRealloc = drmp3__realloc_default;
62844 allocationCallbacks.onFree = drmp3__free_default;
62845 return allocationCallbacks;
62848 static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead)
62850 size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead);
62851 pMP3->streamCursor += bytesRead;
62854 static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin)
62856 DRMP3_ASSERT(offset >= 0);
62857 if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) {
62858 return DRMP3_FALSE;
62860 if (origin == drmp3_seek_origin_start) {
62861 pMP3->streamCursor = (drmp3_uint64)offset;
62863 pMP3->streamCursor += offset;
62867 static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_seek_origin origin)
62869 if (offset <= 0x7FFFFFFF) {
62870 return drmp3__on_seek(pMP3, (int)offset, origin);
62872 if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_start)) {
62873 return DRMP3_FALSE;
62875 offset -= 0x7FFFFFFF;
62876 while (offset > 0) {
62877 if (offset <= 0x7FFFFFFF) {
62878 if (!drmp3__on_seek(pMP3, (int)offset, drmp3_seek_origin_current)) {
62879 return DRMP3_FALSE;
62883 if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_current)) {
62884 return DRMP3_FALSE;
62886 offset -= 0x7FFFFFFF;
62891 static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
62893 drmp3_uint32 pcmFramesRead = 0;
62894 DRMP3_ASSERT(pMP3 != NULL);
62895 DRMP3_ASSERT(pMP3->onRead != NULL);
62900 drmp3dec_frame_info info;
62901 if (pMP3->dataSize < DRMP3_MIN_DATA_CHUNK_SIZE) {
62903 if (pMP3->pData != NULL) {
62904 memmove(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
62906 pMP3->dataConsumed = 0;
62907 if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) {
62908 drmp3_uint8* pNewData;
62910 newDataCap = DRMP3_DATA_CHUNK_SIZE;
62911 pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
62912 if (pNewData == NULL) {
62915 pMP3->pData = pNewData;
62916 pMP3->dataCapacity = newDataCap;
62918 bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
62919 if (bytesRead == 0) {
62920 if (pMP3->dataSize == 0) {
62921 pMP3->atEnd = DRMP3_TRUE;
62925 pMP3->dataSize += bytesRead;
62927 if (pMP3->dataSize > INT_MAX) {
62928 pMP3->atEnd = DRMP3_TRUE;
62931 DRMP3_ASSERT(pMP3->pData != NULL);
62932 DRMP3_ASSERT(pMP3->dataCapacity > 0);
62933 pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info);
62934 if (info.frame_bytes > 0) {
62935 pMP3->dataConsumed += (size_t)info.frame_bytes;
62936 pMP3->dataSize -= (size_t)info.frame_bytes;
62938 if (pcmFramesRead > 0) {
62939 pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header);
62940 pMP3->pcmFramesConsumedInMP3Frame = 0;
62941 pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
62942 pMP3->mp3FrameChannels = info.channels;
62943 pMP3->mp3FrameSampleRate = info.hz;
62945 } else if (info.frame_bytes == 0) {
62947 memmove(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
62948 pMP3->dataConsumed = 0;
62949 if (pMP3->dataCapacity == pMP3->dataSize) {
62950 drmp3_uint8* pNewData;
62952 newDataCap = pMP3->dataCapacity + DRMP3_DATA_CHUNK_SIZE;
62953 pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
62954 if (pNewData == NULL) {
62957 pMP3->pData = pNewData;
62958 pMP3->dataCapacity = newDataCap;
62960 bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
62961 if (bytesRead == 0) {
62962 pMP3->atEnd = DRMP3_TRUE;
62965 pMP3->dataSize += bytesRead;
62968 return pcmFramesRead;
62970 static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
62972 drmp3_uint32 pcmFramesRead = 0;
62973 drmp3dec_frame_info info;
62974 DRMP3_ASSERT(pMP3 != NULL);
62975 DRMP3_ASSERT(pMP3->memory.pData != NULL);
62979 pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info);
62980 if (pcmFramesRead > 0) {
62981 pMP3->pcmFramesConsumedInMP3Frame = 0;
62982 pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
62983 pMP3->mp3FrameChannels = info.channels;
62984 pMP3->mp3FrameSampleRate = info.hz;
62986 pMP3->memory.currentReadPos += (size_t)info.frame_bytes;
62987 return pcmFramesRead;
62989 static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
62991 if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) {
62992 return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames);
62994 return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames);
62997 static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3)
62999 DRMP3_ASSERT(pMP3 != NULL);
63000 return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames);
63003 static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3)
63005 drmp3_uint32 pcmFrameCount;
63006 DRMP3_ASSERT(pMP3 != NULL);
63007 pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL);
63008 if (pcmFrameCount == 0) {
63011 pMP3->currentPCMFrame += pcmFrameCount;
63012 pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount;
63013 pMP3->pcmFramesRemainingInMP3Frame = 0;
63014 return pcmFrameCount;
63017 static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks)
63019 DRMP3_ASSERT(pMP3 != NULL);
63020 DRMP3_ASSERT(onRead != NULL);
63021 drmp3dec_init(&pMP3->decoder);
63022 pMP3->onRead = onRead;
63023 pMP3->onSeek = onSeek;
63024 pMP3->pUserData = pUserData;
63025 pMP3->allocationCallbacks = drmp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
63026 if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) {
63027 return DRMP3_FALSE;
63029 if (!drmp3_decode_next_frame(pMP3)) {
63030 drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
63031 return DRMP3_FALSE;
63033 pMP3->channels = pMP3->mp3FrameChannels;
63034 pMP3->sampleRate = pMP3->mp3FrameSampleRate;
63037 DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks)
63039 if (pMP3 == NULL || onRead == NULL) {
63040 return DRMP3_FALSE;
63042 DRMP3_ZERO_OBJECT(pMP3);
63043 return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks);
63045 static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
63047 drmp3* pMP3 = (drmp3*)pUserData;
63048 size_t bytesRemaining;
63049 DRMP3_ASSERT(pMP3 != NULL);
63050 DRMP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos);
63051 bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos;
63052 if (bytesToRead > bytesRemaining) {
63053 bytesToRead = bytesRemaining;
63055 if (bytesToRead > 0) {
63056 DRMP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead);
63057 pMP3->memory.currentReadPos += bytesToRead;
63059 return bytesToRead;
63061 static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin)
63063 drmp3* pMP3 = (drmp3*)pUserData;
63064 DRMP3_ASSERT(pMP3 != NULL);
63065 if (origin == drmp3_seek_origin_current) {
63066 if (byteOffset > 0) {
63067 if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) {
63068 byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos);
63071 if (pMP3->memory.currentReadPos < (size_t)-byteOffset) {
63072 byteOffset = -(int)pMP3->memory.currentReadPos;
63075 pMP3->memory.currentReadPos += byteOffset;
63077 if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) {
63078 pMP3->memory.currentReadPos = byteOffset;
63080 pMP3->memory.currentReadPos = pMP3->memory.dataSize;
63085 DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks)
63087 if (pMP3 == NULL) {
63088 return DRMP3_FALSE;
63090 DRMP3_ZERO_OBJECT(pMP3);
63091 if (pData == NULL || dataSize == 0) {
63092 return DRMP3_FALSE;
63094 pMP3->memory.pData = (const drmp3_uint8*)pData;
63095 pMP3->memory.dataSize = dataSize;
63096 pMP3->memory.currentReadPos = 0;
63097 return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pAllocationCallbacks);
63099 #ifndef DR_MP3_NO_STDIO
63103 static drmp3_result drmp3_result_from_errno(int e)
63107 case 0: return DRMP3_SUCCESS;
63109 case EPERM: return DRMP3_INVALID_OPERATION;
63112 case ENOENT: return DRMP3_DOES_NOT_EXIST;
63115 case ESRCH: return DRMP3_DOES_NOT_EXIST;
63118 case EINTR: return DRMP3_INTERRUPT;
63121 case EIO: return DRMP3_IO_ERROR;
63124 case ENXIO: return DRMP3_DOES_NOT_EXIST;
63127 case E2BIG: return DRMP3_INVALID_ARGS;
63130 case ENOEXEC: return DRMP3_INVALID_FILE;
63133 case EBADF: return DRMP3_INVALID_FILE;
63136 case ECHILD: return DRMP3_ERROR;
63139 case EAGAIN: return DRMP3_UNAVAILABLE;
63142 case ENOMEM: return DRMP3_OUT_OF_MEMORY;
63145 case EACCES: return DRMP3_ACCESS_DENIED;
63148 case EFAULT: return DRMP3_BAD_ADDRESS;
63151 case ENOTBLK: return DRMP3_ERROR;
63154 case EBUSY: return DRMP3_BUSY;
63157 case EEXIST: return DRMP3_ALREADY_EXISTS;
63160 case EXDEV: return DRMP3_ERROR;
63163 case ENODEV: return DRMP3_DOES_NOT_EXIST;
63166 case ENOTDIR: return DRMP3_NOT_DIRECTORY;
63169 case EISDIR: return DRMP3_IS_DIRECTORY;
63172 case EINVAL: return DRMP3_INVALID_ARGS;
63175 case ENFILE: return DRMP3_TOO_MANY_OPEN_FILES;
63178 case EMFILE: return DRMP3_TOO_MANY_OPEN_FILES;
63181 case ENOTTY: return DRMP3_INVALID_OPERATION;
63184 case ETXTBSY: return DRMP3_BUSY;
63187 case EFBIG: return DRMP3_TOO_BIG;
63190 case ENOSPC: return DRMP3_NO_SPACE;
63193 case ESPIPE: return DRMP3_BAD_SEEK;
63196 case EROFS: return DRMP3_ACCESS_DENIED;
63199 case EMLINK: return DRMP3_TOO_MANY_LINKS;
63202 case EPIPE: return DRMP3_BAD_PIPE;
63205 case EDOM: return DRMP3_OUT_OF_RANGE;
63208 case ERANGE: return DRMP3_OUT_OF_RANGE;
63211 case EDEADLK: return DRMP3_DEADLOCK;
63213 #ifdef ENAMETOOLONG
63214 case ENAMETOOLONG: return DRMP3_PATH_TOO_LONG;
63217 case ENOLCK: return DRMP3_ERROR;
63220 case ENOSYS: return DRMP3_NOT_IMPLEMENTED;
63223 case ENOTEMPTY: return DRMP3_DIRECTORY_NOT_EMPTY;
63226 case ELOOP: return DRMP3_TOO_MANY_LINKS;
63229 case ENOMSG: return DRMP3_NO_MESSAGE;
63232 case EIDRM: return DRMP3_ERROR;
63235 case ECHRNG: return DRMP3_ERROR;
63238 case EL2NSYNC: return DRMP3_ERROR;
63241 case EL3HLT: return DRMP3_ERROR;
63244 case EL3RST: return DRMP3_ERROR;
63247 case ELNRNG: return DRMP3_OUT_OF_RANGE;
63250 case EUNATCH: return DRMP3_ERROR;
63253 case ENOCSI: return DRMP3_ERROR;
63256 case EL2HLT: return DRMP3_ERROR;
63259 case EBADE: return DRMP3_ERROR;
63262 case EBADR: return DRMP3_ERROR;
63265 case EXFULL: return DRMP3_ERROR;
63268 case ENOANO: return DRMP3_ERROR;
63271 case EBADRQC: return DRMP3_ERROR;
63274 case EBADSLT: return DRMP3_ERROR;
63277 case EBFONT: return DRMP3_INVALID_FILE;
63280 case ENOSTR: return DRMP3_ERROR;
63283 case ENODATA: return DRMP3_NO_DATA_AVAILABLE;
63286 case ETIME: return DRMP3_TIMEOUT;
63289 case ENOSR: return DRMP3_NO_DATA_AVAILABLE;
63292 case ENONET: return DRMP3_NO_NETWORK;
63295 case ENOPKG: return DRMP3_ERROR;
63298 case EREMOTE: return DRMP3_ERROR;
63301 case ENOLINK: return DRMP3_ERROR;
63304 case EADV: return DRMP3_ERROR;
63307 case ESRMNT: return DRMP3_ERROR;
63310 case ECOMM: return DRMP3_ERROR;
63313 case EPROTO: return DRMP3_ERROR;
63316 case EMULTIHOP: return DRMP3_ERROR;
63319 case EDOTDOT: return DRMP3_ERROR;
63322 case EBADMSG: return DRMP3_BAD_MESSAGE;
63325 case EOVERFLOW: return DRMP3_TOO_BIG;
63328 case ENOTUNIQ: return DRMP3_NOT_UNIQUE;
63331 case EBADFD: return DRMP3_ERROR;
63334 case EREMCHG: return DRMP3_ERROR;
63337 case ELIBACC: return DRMP3_ACCESS_DENIED;
63340 case ELIBBAD: return DRMP3_INVALID_FILE;
63343 case ELIBSCN: return DRMP3_INVALID_FILE;
63346 case ELIBMAX: return DRMP3_ERROR;
63349 case ELIBEXEC: return DRMP3_ERROR;
63352 case EILSEQ: return DRMP3_INVALID_DATA;
63355 case ERESTART: return DRMP3_ERROR;
63358 case ESTRPIPE: return DRMP3_ERROR;
63361 case EUSERS: return DRMP3_ERROR;
63364 case ENOTSOCK: return DRMP3_NOT_SOCKET;
63366 #ifdef EDESTADDRREQ
63367 case EDESTADDRREQ: return DRMP3_NO_ADDRESS;
63370 case EMSGSIZE: return DRMP3_TOO_BIG;
63373 case EPROTOTYPE: return DRMP3_BAD_PROTOCOL;
63376 case ENOPROTOOPT: return DRMP3_PROTOCOL_UNAVAILABLE;
63378 #ifdef EPROTONOSUPPORT
63379 case EPROTONOSUPPORT: return DRMP3_PROTOCOL_NOT_SUPPORTED;
63381 #ifdef ESOCKTNOSUPPORT
63382 case ESOCKTNOSUPPORT: return DRMP3_SOCKET_NOT_SUPPORTED;
63385 case EOPNOTSUPP: return DRMP3_INVALID_OPERATION;
63387 #ifdef EPFNOSUPPORT
63388 case EPFNOSUPPORT: return DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED;
63390 #ifdef EAFNOSUPPORT
63391 case EAFNOSUPPORT: return DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED;
63394 case EADDRINUSE: return DRMP3_ALREADY_IN_USE;
63396 #ifdef EADDRNOTAVAIL
63397 case EADDRNOTAVAIL: return DRMP3_ERROR;
63400 case ENETDOWN: return DRMP3_NO_NETWORK;
63403 case ENETUNREACH: return DRMP3_NO_NETWORK;
63406 case ENETRESET: return DRMP3_NO_NETWORK;
63408 #ifdef ECONNABORTED
63409 case ECONNABORTED: return DRMP3_NO_NETWORK;
63412 case ECONNRESET: return DRMP3_CONNECTION_RESET;
63415 case ENOBUFS: return DRMP3_NO_SPACE;
63418 case EISCONN: return DRMP3_ALREADY_CONNECTED;
63421 case ENOTCONN: return DRMP3_NOT_CONNECTED;
63424 case ESHUTDOWN: return DRMP3_ERROR;
63426 #ifdef ETOOMANYREFS
63427 case ETOOMANYREFS: return DRMP3_ERROR;
63430 case ETIMEDOUT: return DRMP3_TIMEOUT;
63432 #ifdef ECONNREFUSED
63433 case ECONNREFUSED: return DRMP3_CONNECTION_REFUSED;
63436 case EHOSTDOWN: return DRMP3_NO_HOST;
63438 #ifdef EHOSTUNREACH
63439 case EHOSTUNREACH: return DRMP3_NO_HOST;
63442 case EALREADY: return DRMP3_IN_PROGRESS;
63445 case EINPROGRESS: return DRMP3_IN_PROGRESS;
63448 case ESTALE: return DRMP3_INVALID_FILE;
63451 case EUCLEAN: return DRMP3_ERROR;
63454 case ENOTNAM: return DRMP3_ERROR;
63457 case ENAVAIL: return DRMP3_ERROR;
63460 case EISNAM: return DRMP3_ERROR;
63463 case EREMOTEIO: return DRMP3_IO_ERROR;
63466 case EDQUOT: return DRMP3_NO_SPACE;
63469 case ENOMEDIUM: return DRMP3_DOES_NOT_EXIST;
63472 case EMEDIUMTYPE: return DRMP3_ERROR;
63475 case ECANCELED: return DRMP3_CANCELLED;
63478 case ENOKEY: return DRMP3_ERROR;
63481 case EKEYEXPIRED: return DRMP3_ERROR;
63484 case EKEYREVOKED: return DRMP3_ERROR;
63486 #ifdef EKEYREJECTED
63487 case EKEYREJECTED: return DRMP3_ERROR;
63490 case EOWNERDEAD: return DRMP3_ERROR;
63492 #ifdef ENOTRECOVERABLE
63493 case ENOTRECOVERABLE: return DRMP3_ERROR;
63496 case ERFKILL: return DRMP3_ERROR;
63499 case EHWPOISON: return DRMP3_ERROR;
63501 default: return DRMP3_ERROR;
63504 static drmp3_result drmp3_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
63506 #if defined(_MSC_VER) && _MSC_VER >= 1400
63509 if (ppFile != NULL) {
63512 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
63513 return DRMP3_INVALID_ARGS;
63515 #if defined(_MSC_VER) && _MSC_VER >= 1400
63516 err = fopen_s(ppFile, pFilePath, pOpenMode);
63518 return drmp3_result_from_errno(err);
63521 #if defined(_WIN32) || defined(__APPLE__)
63522 *ppFile = fopen(pFilePath, pOpenMode);
63524 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
63525 *ppFile = fopen64(pFilePath, pOpenMode);
63527 *ppFile = fopen(pFilePath, pOpenMode);
63530 if (*ppFile == NULL) {
63531 drmp3_result result = drmp3_result_from_errno(errno);
63532 if (result == DRMP3_SUCCESS) {
63533 result = DRMP3_ERROR;
63538 return DRMP3_SUCCESS;
63540 #if defined(_WIN32)
63541 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
63542 #define DRMP3_HAS_WFOPEN
63545 static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drmp3_allocation_callbacks* pAllocationCallbacks)
63547 if (ppFile != NULL) {
63550 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
63551 return DRMP3_INVALID_ARGS;
63553 #if defined(DRMP3_HAS_WFOPEN)
63555 #if defined(_MSC_VER) && _MSC_VER >= 1400
63556 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
63558 return drmp3_result_from_errno(err);
63561 *ppFile = _wfopen(pFilePath, pOpenMode);
63562 if (*ppFile == NULL) {
63563 return drmp3_result_from_errno(errno);
63566 (void)pAllocationCallbacks;
63572 const wchar_t* pFilePathTemp = pFilePath;
63573 char* pFilePathMB = NULL;
63574 char pOpenModeMB[32] = {0};
63575 DRMP3_ZERO_OBJECT(&mbs);
63576 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
63577 if (lenMB == (size_t)-1) {
63578 return drmp3_result_from_errno(errno);
63580 pFilePathMB = (char*)drmp3__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);
63581 if (pFilePathMB == NULL) {
63582 return DRMP3_OUT_OF_MEMORY;
63584 pFilePathTemp = pFilePath;
63585 DRMP3_ZERO_OBJECT(&mbs);
63586 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
63590 if (pOpenMode[i] == 0) {
63591 pOpenModeMB[i] = '\0';
63594 pOpenModeMB[i] = (char)pOpenMode[i];
63598 *ppFile = fopen(pFilePathMB, pOpenModeMB);
63599 drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
63601 if (*ppFile == NULL) {
63602 return DRMP3_ERROR;
63605 return DRMP3_SUCCESS;
63607 static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
63609 return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
63611 static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin)
63613 return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
63615 DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
63617 drmp3_bool32 result;
63619 if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) {
63620 return DRMP3_FALSE;
63622 result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
63623 if (result != DRMP3_TRUE) {
63629 DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
63631 drmp3_bool32 result;
63633 if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) {
63634 return DRMP3_FALSE;
63636 result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
63637 if (result != DRMP3_TRUE) {
63644 DRMP3_API void drmp3_uninit(drmp3* pMP3)
63646 if (pMP3 == NULL) {
63649 #ifndef DR_MP3_NO_STDIO
63650 if (pMP3->onRead == drmp3__on_read_stdio) {
63651 FILE* pFile = (FILE*)pMP3->pUserData;
63652 if (pFile != NULL) {
63654 pMP3->pUserData = NULL;
63658 drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
63660 #if defined(DR_MP3_FLOAT_OUTPUT)
63661 static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sampleCount)
63665 drmp3_uint64 sampleCount4;
63667 sampleCount4 = sampleCount >> 2;
63668 for (i4 = 0; i4 < sampleCount4; i4 += 1) {
63669 float x0 = src[i+0];
63670 float x1 = src[i+1];
63671 float x2 = src[i+2];
63672 float x3 = src[i+3];
63673 x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
63674 x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
63675 x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
63676 x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
63677 x0 = x0 * 32767.0f;
63678 x1 = x1 * 32767.0f;
63679 x2 = x2 * 32767.0f;
63680 x3 = x3 * 32767.0f;
63681 dst[i+0] = (drmp3_int16)x0;
63682 dst[i+1] = (drmp3_int16)x1;
63683 dst[i+2] = (drmp3_int16)x2;
63684 dst[i+3] = (drmp3_int16)x3;
63687 for (; i < sampleCount; i += 1) {
63689 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
63691 dst[i] = (drmp3_int16)x;
63695 #if !defined(DR_MP3_FLOAT_OUTPUT)
63696 static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sampleCount)
63699 for (i = 0; i < sampleCount; i += 1) {
63700 float x = (float)src[i];
63701 x = x * 0.000030517578125f;
63706 static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesToRead, void* pBufferOut)
63708 drmp3_uint64 totalFramesRead = 0;
63709 DRMP3_ASSERT(pMP3 != NULL);
63710 DRMP3_ASSERT(pMP3->onRead != NULL);
63711 while (framesToRead > 0) {
63712 drmp3_uint32 framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead);
63713 if (pBufferOut != NULL) {
63714 #if defined(DR_MP3_FLOAT_OUTPUT)
63715 float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels);
63716 float* pFramesInF32 = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
63717 DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels);
63719 drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalFramesRead * pMP3->channels);
63720 drmp3_int16* pFramesInS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
63721 DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels);
63724 pMP3->currentPCMFrame += framesToConsume;
63725 pMP3->pcmFramesConsumedInMP3Frame += framesToConsume;
63726 pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume;
63727 totalFramesRead += framesToConsume;
63728 framesToRead -= framesToConsume;
63729 if (framesToRead == 0) {
63732 DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0);
63733 if (drmp3_decode_next_frame(pMP3) == 0) {
63737 return totalFramesRead;
63739 DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut)
63741 if (pMP3 == NULL || pMP3->onRead == NULL) {
63744 #if defined(DR_MP3_FLOAT_OUTPUT)
63745 return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
63748 drmp3_int16 pTempS16[8192];
63749 drmp3_uint64 totalPCMFramesRead = 0;
63750 while (totalPCMFramesRead < framesToRead) {
63751 drmp3_uint64 framesJustRead;
63752 drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
63753 drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels;
63754 if (framesToReadNow > framesRemaining) {
63755 framesToReadNow = framesRemaining;
63757 framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16);
63758 if (framesJustRead == 0) {
63761 drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels);
63762 totalPCMFramesRead += framesJustRead;
63764 return totalPCMFramesRead;
63768 DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut)
63770 if (pMP3 == NULL || pMP3->onRead == NULL) {
63773 #if !defined(DR_MP3_FLOAT_OUTPUT)
63774 return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
63777 float pTempF32[4096];
63778 drmp3_uint64 totalPCMFramesRead = 0;
63779 while (totalPCMFramesRead < framesToRead) {
63780 drmp3_uint64 framesJustRead;
63781 drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
63782 drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempF32) / pMP3->channels;
63783 if (framesToReadNow > framesRemaining) {
63784 framesToReadNow = framesRemaining;
63786 framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32);
63787 if (framesJustRead == 0) {
63790 drmp3_f32_to_s16((drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels);
63791 totalPCMFramesRead += framesJustRead;
63793 return totalPCMFramesRead;
63797 static void drmp3_reset(drmp3* pMP3)
63799 DRMP3_ASSERT(pMP3 != NULL);
63800 pMP3->pcmFramesConsumedInMP3Frame = 0;
63801 pMP3->pcmFramesRemainingInMP3Frame = 0;
63802 pMP3->currentPCMFrame = 0;
63803 pMP3->dataSize = 0;
63804 pMP3->atEnd = DRMP3_FALSE;
63805 drmp3dec_init(&pMP3->decoder);
63807 static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3)
63809 DRMP3_ASSERT(pMP3 != NULL);
63810 DRMP3_ASSERT(pMP3->onSeek != NULL);
63811 if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) {
63812 return DRMP3_FALSE;
63817 static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset)
63819 drmp3_uint64 framesRead;
63820 #if defined(DR_MP3_FLOAT_OUTPUT)
63821 framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL);
63823 framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL);
63825 if (framesRead != frameOffset) {
63826 return DRMP3_FALSE;
63830 static drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex)
63832 DRMP3_ASSERT(pMP3 != NULL);
63833 if (frameIndex == pMP3->currentPCMFrame) {
63836 if (frameIndex < pMP3->currentPCMFrame) {
63837 if (!drmp3_seek_to_start_of_stream(pMP3)) {
63838 return DRMP3_FALSE;
63841 DRMP3_ASSERT(frameIndex >= pMP3->currentPCMFrame);
63842 return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame));
63844 static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex)
63846 drmp3_uint32 iSeekPoint;
63847 DRMP3_ASSERT(pSeekPointIndex != NULL);
63848 *pSeekPointIndex = 0;
63849 if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) {
63850 return DRMP3_FALSE;
63852 for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) {
63853 if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) {
63856 *pSeekPointIndex = iSeekPoint;
63860 static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex)
63862 drmp3_seek_point seekPoint;
63863 drmp3_uint32 priorSeekPointIndex;
63864 drmp3_uint16 iMP3Frame;
63865 drmp3_uint64 leftoverFrames;
63866 DRMP3_ASSERT(pMP3 != NULL);
63867 DRMP3_ASSERT(pMP3->pSeekPoints != NULL);
63868 DRMP3_ASSERT(pMP3->seekPointCount > 0);
63869 if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) {
63870 seekPoint = pMP3->pSeekPoints[priorSeekPointIndex];
63872 seekPoint.seekPosInBytes = 0;
63873 seekPoint.pcmFrameIndex = 0;
63874 seekPoint.mp3FramesToDiscard = 0;
63875 seekPoint.pcmFramesToDiscard = 0;
63877 if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) {
63878 return DRMP3_FALSE;
63881 for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) {
63882 drmp3_uint32 pcmFramesRead;
63883 drmp3d_sample_t* pPCMFrames;
63885 if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) {
63886 pPCMFrames = (drmp3d_sample_t*)pMP3->pcmFrames;
63888 pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames);
63889 if (pcmFramesRead == 0) {
63890 return DRMP3_FALSE;
63893 pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard;
63894 leftoverFrames = frameIndex - pMP3->currentPCMFrame;
63895 return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames);
63897 DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex)
63899 if (pMP3 == NULL || pMP3->onSeek == NULL) {
63900 return DRMP3_FALSE;
63902 if (frameIndex == 0) {
63903 return drmp3_seek_to_start_of_stream(pMP3);
63905 if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) {
63906 return drmp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex);
63908 return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex);
63911 DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount)
63913 drmp3_uint64 currentPCMFrame;
63914 drmp3_uint64 totalPCMFrameCount;
63915 drmp3_uint64 totalMP3FrameCount;
63916 if (pMP3 == NULL) {
63917 return DRMP3_FALSE;
63919 if (pMP3->onSeek == NULL) {
63920 return DRMP3_FALSE;
63922 currentPCMFrame = pMP3->currentPCMFrame;
63923 if (!drmp3_seek_to_start_of_stream(pMP3)) {
63924 return DRMP3_FALSE;
63926 totalPCMFrameCount = 0;
63927 totalMP3FrameCount = 0;
63929 drmp3_uint32 pcmFramesInCurrentMP3Frame;
63930 pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL);
63931 if (pcmFramesInCurrentMP3Frame == 0) {
63934 totalPCMFrameCount += pcmFramesInCurrentMP3Frame;
63935 totalMP3FrameCount += 1;
63937 if (!drmp3_seek_to_start_of_stream(pMP3)) {
63938 return DRMP3_FALSE;
63940 if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
63941 return DRMP3_FALSE;
63943 if (pMP3FrameCount != NULL) {
63944 *pMP3FrameCount = totalMP3FrameCount;
63946 if (pPCMFrameCount != NULL) {
63947 *pPCMFrameCount = totalPCMFrameCount;
63951 DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3)
63953 drmp3_uint64 totalPCMFrameCount;
63954 if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) {
63957 return totalPCMFrameCount;
63959 DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3)
63961 drmp3_uint64 totalMP3FrameCount;
63962 if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) {
63965 return totalMP3FrameCount;
63967 static void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, drmp3_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart)
63970 float pcmFrameCountOutF;
63971 drmp3_uint32 pcmFrameCountOut;
63972 srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate;
63973 DRMP3_ASSERT(srcRatio > 0);
63974 pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio);
63975 pcmFrameCountOut = (drmp3_uint32)pcmFrameCountOutF;
63976 *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut;
63977 *pRunningPCMFrameCount += pcmFrameCountOut;
63981 drmp3_uint64 bytePos;
63982 drmp3_uint64 pcmFrameIndex;
63983 } drmp3__seeking_mp3_frame_info;
63984 DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints)
63986 drmp3_uint32 seekPointCount;
63987 drmp3_uint64 currentPCMFrame;
63988 drmp3_uint64 totalMP3FrameCount;
63989 drmp3_uint64 totalPCMFrameCount;
63990 if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) {
63991 return DRMP3_FALSE;
63993 seekPointCount = *pSeekPointCount;
63994 if (seekPointCount == 0) {
63995 return DRMP3_FALSE;
63997 currentPCMFrame = pMP3->currentPCMFrame;
63998 if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) {
63999 return DRMP3_FALSE;
64001 if (totalMP3FrameCount < DRMP3_SEEK_LEADING_MP3_FRAMES+1) {
64002 seekPointCount = 1;
64003 pSeekPoints[0].seekPosInBytes = 0;
64004 pSeekPoints[0].pcmFrameIndex = 0;
64005 pSeekPoints[0].mp3FramesToDiscard = 0;
64006 pSeekPoints[0].pcmFramesToDiscard = 0;
64008 drmp3_uint64 pcmFramesBetweenSeekPoints;
64009 drmp3__seeking_mp3_frame_info mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES+1];
64010 drmp3_uint64 runningPCMFrameCount = 0;
64011 float runningPCMFrameCountFractionalPart = 0;
64012 drmp3_uint64 nextTargetPCMFrame;
64013 drmp3_uint32 iMP3Frame;
64014 drmp3_uint32 iSeekPoint;
64015 if (seekPointCount > totalMP3FrameCount-1) {
64016 seekPointCount = (drmp3_uint32)totalMP3FrameCount-1;
64018 pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1);
64019 if (!drmp3_seek_to_start_of_stream(pMP3)) {
64020 return DRMP3_FALSE;
64022 for (iMP3Frame = 0; iMP3Frame < DRMP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) {
64023 drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
64024 DRMP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize);
64025 mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize;
64026 mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount;
64027 pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL);
64028 if (pcmFramesInCurrentMP3FrameIn == 0) {
64029 return DRMP3_FALSE;
64031 drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
64033 nextTargetPCMFrame = 0;
64034 for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) {
64035 nextTargetPCMFrame += pcmFramesBetweenSeekPoints;
64037 if (nextTargetPCMFrame < runningPCMFrameCount) {
64038 pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
64039 pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
64040 pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES;
64041 pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
64045 drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
64046 for (i = 0; i < DRMP3_COUNTOF(mp3FrameInfo)-1; ++i) {
64047 mp3FrameInfo[i] = mp3FrameInfo[i+1];
64049 mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize;
64050 mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount;
64051 pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL);
64052 if (pcmFramesInCurrentMP3FrameIn == 0) {
64053 pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
64054 pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
64055 pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES;
64056 pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
64059 drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
64063 if (!drmp3_seek_to_start_of_stream(pMP3)) {
64064 return DRMP3_FALSE;
64066 if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
64067 return DRMP3_FALSE;
64070 *pSeekPointCount = seekPointCount;
64073 DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints)
64075 if (pMP3 == NULL) {
64076 return DRMP3_FALSE;
64078 if (seekPointCount == 0 || pSeekPoints == NULL) {
64079 pMP3->seekPointCount = 0;
64080 pMP3->pSeekPoints = NULL;
64082 pMP3->seekPointCount = seekPointCount;
64083 pMP3->pSeekPoints = pSeekPoints;
64087 static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
64089 drmp3_uint64 totalFramesRead = 0;
64090 drmp3_uint64 framesCapacity = 0;
64091 float* pFrames = NULL;
64093 DRMP3_ASSERT(pMP3 != NULL);
64095 drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels;
64096 drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp);
64097 if (framesJustRead == 0) {
64100 if (framesCapacity < totalFramesRead + framesJustRead) {
64101 drmp3_uint64 oldFramesBufferSize;
64102 drmp3_uint64 newFramesBufferSize;
64103 drmp3_uint64 newFramesCap;
64105 newFramesCap = framesCapacity * 2;
64106 if (newFramesCap < totalFramesRead + framesJustRead) {
64107 newFramesCap = totalFramesRead + framesJustRead;
64109 oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float);
64110 newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float);
64111 if (newFramesBufferSize > DRMP3_SIZE_MAX) {
64114 pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
64115 if (pNewFrames == NULL) {
64116 drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
64119 pFrames = pNewFrames;
64120 framesCapacity = newFramesCap;
64122 DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float)));
64123 totalFramesRead += framesJustRead;
64124 if (framesJustRead != framesToReadRightNow) {
64128 if (pConfig != NULL) {
64129 pConfig->channels = pMP3->channels;
64130 pConfig->sampleRate = pMP3->sampleRate;
64132 drmp3_uninit(pMP3);
64133 if (pTotalFrameCount) {
64134 *pTotalFrameCount = totalFramesRead;
64138 static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
64140 drmp3_uint64 totalFramesRead = 0;
64141 drmp3_uint64 framesCapacity = 0;
64142 drmp3_int16* pFrames = NULL;
64143 drmp3_int16 temp[4096];
64144 DRMP3_ASSERT(pMP3 != NULL);
64146 drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels;
64147 drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp);
64148 if (framesJustRead == 0) {
64151 if (framesCapacity < totalFramesRead + framesJustRead) {
64152 drmp3_uint64 newFramesBufferSize;
64153 drmp3_uint64 oldFramesBufferSize;
64154 drmp3_uint64 newFramesCap;
64155 drmp3_int16* pNewFrames;
64156 newFramesCap = framesCapacity * 2;
64157 if (newFramesCap < totalFramesRead + framesJustRead) {
64158 newFramesCap = totalFramesRead + framesJustRead;
64160 oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(drmp3_int16);
64161 newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(drmp3_int16);
64162 if (newFramesBufferSize > DRMP3_SIZE_MAX) {
64165 pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
64166 if (pNewFrames == NULL) {
64167 drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
64170 pFrames = pNewFrames;
64171 framesCapacity = newFramesCap;
64173 DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(drmp3_int16)));
64174 totalFramesRead += framesJustRead;
64175 if (framesJustRead != framesToReadRightNow) {
64179 if (pConfig != NULL) {
64180 pConfig->channels = pMP3->channels;
64181 pConfig->sampleRate = pMP3->sampleRate;
64183 drmp3_uninit(pMP3);
64184 if (pTotalFrameCount) {
64185 *pTotalFrameCount = totalFramesRead;
64189 DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
64192 if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {
64195 return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
64197 DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
64200 if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {
64203 return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
64205 DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
64208 if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
64211 return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
64213 DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
64216 if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
64219 return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
64221 #ifndef DR_MP3_NO_STDIO
64222 DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
64225 if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
64228 return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
64230 DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
64233 if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
64236 return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
64239 DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks)
64241 if (pAllocationCallbacks != NULL) {
64242 return drmp3__malloc_from_callbacks(sz, pAllocationCallbacks);
64244 return drmp3__malloc_default(sz, NULL);
64247 DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks)
64249 if (pAllocationCallbacks != NULL) {
64250 drmp3__free_from_callbacks(p, pAllocationCallbacks);
64252 drmp3__free_default(p, NULL);
64257 #endif /* DRMP3_IMPLEMENTATION */
64258 #endif /* MA_NO_MP3 */
64261 /* End globally disabled warnings. */
64262 #if defined(_MSC_VER)
64263 #pragma warning(pop)
64266 #endif /* miniaudio_c */
64267 #endif /* MINIAUDIO_IMPLEMENTATION */
64270 RELEASE NOTES - VERSION 0.10.x
64271 ==============================
64272 Version 0.10 includes major API changes and refactoring, mostly concerned with the data conversion system. Data conversion is performed internally to convert
64273 audio 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
64274 to the `ma_decoder` object. The previous design has several design flaws and missing features which necessitated a complete redesign.
64277 Changes to Data Conversion
64278 --------------------------
64279 The previous data conversion system used callbacks to deliver input data for conversion. This design works well in some specific situations, but in other
64280 situations 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
64281 pointer to the input data directly rather than dealing with a callback.
64283 The following are the data conversion APIs that have been removed and their replacements:
64285 - ma_format_converter -> ma_convert_pcm_frames_format()
64286 - ma_channel_router -> ma_channel_converter
64287 - ma_src -> ma_resampler
64288 - ma_pcm_converter -> ma_data_converter
64290 The 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
64291 `*_process_pcm_frames()` function as a pointer to a buffer.
64293 The simplest aspect of data conversion is sample format conversion. To convert between two formats, just call `ma_convert_pcm_frames_format()`. Channel
64294 conversion is also simple which you can do with `ma_channel_converter` via `ma_channel_converter_process_pcm_frames()`.
64296 Resampling 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
64297 call `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
64298 output. Upon returning they will receive the number of input frames that were consumed and the number of output frames that were generated.
64300 The `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
64301 makes it the best option if you need to do data conversion.
64303 In addition to changes to the API design, a few other changes have been made to the data conversion pipeline:
64305 - The sinc resampler has been removed. This was completely broken and never actually worked properly.
64306 - 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
64307 `lpfOrder` option, which has a maximum value of MA_MAX_FILTER_ORDER.
64308 - Data conversion now supports s16 natively which runs through a fixed point pipeline. Previously everything needed to be converted to floating point before
64309 processing, whereas now both s16 and f32 are natively supported. Other formats still require conversion to either s16 or f32 prior to processing, however
64310 `ma_data_converter` will handle this for you.
64313 Custom Memory Allocators
64314 ------------------------
64315 miniaudio has always supported macro level customization for memory allocation via MA_MALLOC, MA_REALLOC and MA_FREE, however some scenarios require more
64316 flexibility 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
64317 `ma_allocation_callbacks` structure. Anything making use of heap allocations has been updated to accept this new structure.
64319 The `ma_context_config` structure has been updated with a new member called `allocationCallbacks`. Leaving this set to it's defaults returned by
64320 `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
64321 way, and leaving everything as-is after `ma_decoder_config_init()` will cause it to use the same defaults.
64323 The 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.
64324 Otherwise they will use the relevant callback in the structure.
64329 - ma_aligned_malloc()
64330 - ma_aligned_free()
64331 - ma_rb_init() / ma_rb_init_ex()
64332 - ma_pcm_rb_init() / ma_pcm_rb_init_ex()
64334 Note 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
64335 allocation callbacks.
64338 Buffer and Period Configuration Changes
64339 ---------------------------------------
64340 The 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
64341 `bufferSizeInFrames` and `bufferSizeInMilliseconds` defined the size of the entire buffer, with the size of a period being the size of this variable divided by
64342 the period count. This became confusing because people would expect the value of `bufferSizeInFrames` or `bufferSizeInMilliseconds` to independantly determine
64343 latency, 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
64344 `periodSizeInFrames` and `periodSizeInMilliseconds`.
64346 These 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
64347 that 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
64350 The following unused APIs have been removed:
64352 ma_get_default_buffer_size_in_milliseconds()
64353 ma_get_default_buffer_size_in_frames()
64355 The following macros have been removed:
64357 MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY
64358 MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE
64363 Other less major API changes have also been made in version 0.10.
64365 `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.
64367 The `ma_sine_wave` API has been replaced with a more general API called `ma_waveform`. This supports generation of different types of waveforms, including
64368 sine, square, triangle and sawtooth. Use `ma_waveform_init()` in place of `ma_sine_wave_init()` to initialize the waveform object. This takes a configuration
64369 object called `ma_waveform_config` which defines the properties of the waveform. Use `ma_waveform_config_init()` to initialize a `ma_waveform_config` object.
64370 Use `ma_waveform_read_pcm_frames()` in place of `ma_sine_wave_read_f32()` and `ma_sine_wave_read_f32_ex()`.
64372 `ma_convert_frames()` and `ma_convert_frames_ex()` have been changed. Both of these functions now take a new parameter called `frameCountOut` which specifies
64373 the 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
64374 take 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
64375 prevent the parameter list getting too long, and to prevent API breakage whenever a new conversion property is added.
64377 `ma_calculate_frame_count_after_src()` has been renamed to `ma_calculate_frame_count_after_resampling()` for consistency with the new `ma_resampler` API.
64382 The following filters have been added:
64384 |-------------|-------------------------------------------------------------------|
64385 | API | Description |
64386 |-------------|-------------------------------------------------------------------|
64387 | ma_biquad | Biquad filter (transposed direct form 2) |
64388 | ma_lpf1 | First order low-pass filter |
64389 | ma_lpf2 | Second order low-pass filter |
64390 | ma_lpf | High order low-pass filter (Butterworth) |
64391 | ma_hpf1 | First order high-pass filter |
64392 | ma_hpf2 | Second order high-pass filter |
64393 | ma_hpf | High order high-pass filter (Butterworth) |
64394 | ma_bpf2 | Second order band-pass filter |
64395 | ma_bpf | High order band-pass filter |
64396 | ma_peak2 | Second order peaking filter |
64397 | ma_notch2 | Second order notching filter |
64398 | ma_loshelf2 | Second order low shelf filter |
64399 | ma_hishelf2 | Second order high shelf filter |
64400 |-------------|-------------------------------------------------------------------|
64402 These filters all support 32-bit floating point and 16-bit signed integer formats natively. Other formats need to be converted beforehand.
64405 Sine, Square, Triangle and Sawtooth Waveforms
64406 ---------------------------------------------
64407 Previously miniaudio supported only sine wave generation. This has now been generalized to support sine, square, triangle and sawtooth waveforms. The old
64408 `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
64409 into `ma_waveform_init()`. Then use `ma_waveform_read_pcm_frames()` to read PCM data.
64414 A 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
64415 similar 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`
64416 object. Then use `ma_noise_read_pcm_frames()` to read PCM data.
64419 Miscellaneous Changes
64420 ---------------------
64421 The 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
64424 Internal functions have all been made static where possible. If you get warnings about unused functions, please submit a bug report.
64426 The `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
64427 the 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
64428 was 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,
64429 this has been removed from all structures.
64431 Results codes have been overhauled. Unnecessary result codes have been removed, and some have been renumbered for organisation purposes. If you are are binding
64432 maintainer 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
64433 `ma_result_description()` API.
64435 ALSA: The automatic format conversion, channel conversion and resampling performed by ALSA is now disabled by default as they were causing some compatibility
64436 issues with certain devices and configurations. These can be individually enabled via the device config:
64439 deviceConfig.alsa.noAutoFormat = MA_TRUE;
64440 deviceConfig.alsa.noAutoChannels = MA_TRUE;
64441 deviceConfig.alsa.noAutoResample = MA_TRUE;
64446 RELEASE NOTES - VERSION 0.9.x
64447 =============================
64448 Version 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
64449 would 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
64450 library is still relatively young and unknown.
64452 There'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
64453 hold 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
64454 (though you won't be getting future bug fixes).
64457 Rebranding to "miniaudio"
64458 -------------------------
64459 The decision was made to rename mini_al to miniaudio. Don't worry, it's the same project. The reason for this is simple:
64461 1) Having the word "audio" in the title makes it immediately clear that the library is related to audio; and
64462 2) I don't like the look of the underscore.
64464 This 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
64465 now 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
64466 with at the same time as the full-duplex changes. I'm hoping this will be the last of the major API changes. Fingers crossed!
64468 The implementation define is now "#define MINIAUDIO_IMPLEMENTATION". You can also use "#define MA_IMPLEMENTATION" if that's your preference.
64471 Full-Duplex Support
64472 -------------------
64473 The major feature added to version 0.9 is full-duplex. This has necessitated a few API changes.
64475 1) 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
64476 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
64477 (playback, capture, duplex). The new callback looks like the following:
64479 void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
64481 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
64482 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
64485 2) 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
64486 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
64487 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
64488 playback and capture.
64490 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
64491 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
64492 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
64495 Example device initialization:
64497 ma_device_config config = ma_device_config_init(ma_device_type_duplex); // Or ma_device_type_playback or ma_device_type_capture.
64498 config.playback.pDeviceID = &myPlaybackDeviceID; // Or NULL for the default playback device.
64499 config.playback.format = ma_format_f32;
64500 config.playback.channels = 2;
64501 config.capture.pDeviceID = &myCaptureDeviceID; // Or NULL for the default capture device.
64502 config.capture.format = ma_format_s16;
64503 config.capture.channels = 1;
64504 config.sampleRate = 44100;
64505 config.dataCallback = data_callback;
64506 config.pUserData = &myUserData;
64508 result = ma_device_init(&myContext, &config, &device);
64509 if (result != MA_SUCCESS) {
64510 ... handle error ...
64513 Note that the "onDataCallback" member of ma_device_config has been renamed to "dataCallback". Also, "onStopCallback" has been renamed to "stopCallback".
64515 This 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
64516 the playback device:
64523 In addition to the above, not all platforms have been absolutely thoroughly tested simply because I lack the hardware for such thorough testing. If you
64524 experience a bug, an issue report on GitHub or an email would be greatly appreciated (and a sample program that reproduces the issue if possible).
64529 In addition to the above, the following API changes have been made:
64531 - The log callback is no longer passed to ma_context_config_init(). Instead you need to set it manually after initialization.
64532 - The onLogCallback member of ma_context_config has been renamed to "logCallback".
64533 - 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)
64534 - You can use ma_log_level_to_string() to convert the logLevel to human readable text if you want to log it.
64535 - Some APIs have been renamed:
64536 - mal_decoder_read() -> ma_decoder_read_pcm_frames()
64537 - mal_decoder_seek_to_frame() -> ma_decoder_seek_to_pcm_frame()
64538 - mal_sine_wave_read() -> ma_sine_wave_read_f32()
64539 - mal_sine_wave_read_ex() -> ma_sine_wave_read_f32_ex()
64540 - Some APIs have been removed:
64541 - mal_device_get_buffer_size_in_bytes()
64542 - mal_device_set_recv_callback()
64543 - mal_device_set_send_callback()
64544 - mal_src_set_input_sample_rate()
64545 - mal_src_set_output_sample_rate()
64546 - Error codes have been rearranged. If you're a binding maintainer you will need to update.
64547 - 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
64548 the priority. If you're a binding maintainer you will need to update.
64549 - 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
64551 - 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
64552 count. The rationale for this is to keep it consistent with things like memcpy().
64555 Miscellaneous Changes
64556 ---------------------
64557 The following miscellaneous changes have also been made.
64559 - 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
64560 audio APIs out there, just behind the BSD audio APIs).
64561 - The WebAudio backend has been added. This is based on ScriptProcessorNode. This removes the need for SDL.
64562 - The SDL and OpenAL backends have been removed. These were originally implemented to add support for platforms for which miniaudio was not explicitly
64563 supported. These are no longer needed and have therefore been removed.
64564 - 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
64565 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.
64566 - 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.
64567 - 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
64568 requirement. The rationale for this is to support countries that don't recognize public domain.
64574 v0.10.33 - 2021-04-04
64575 - Core Audio: Fix a memory leak.
64576 - Core Audio: Fix a bug where the performance profile is not being used by playback devices.
64577 - JACK: Fix loading of 64-bit JACK on Windows.
64578 - Fix a calculation error and add a safety check to the following APIs to prevent a division by zero:
64579 - ma_calculate_buffer_size_in_milliseconds_from_frames()
64580 - ma_calculate_buffer_size_in_frames_from_milliseconds()
64581 - Fix compilation errors relating to c89atomic.
64582 - Update FLAC decoder.
64584 v0.10.32 - 2021-02-23
64585 - WASAPI: Fix a deadlock in exclusive mode.
64586 - WASAPI: No longer return an error from ma_context_get_device_info() when an exclusive mode format
64587 cannot be retrieved.
64588 - WASAPI: Attempt to fix some bugs with device uninitialization.
64589 - PulseAudio: Yet another refactor, this time to remove the dependency on `pa_threaded_mainloop`.
64590 - Web Audio: Fix a bug on Chrome and any other browser using the same engine.
64591 - Web Audio: Automatically start the device on some user input if the device has been started. This
64592 is to work around Google's policy of not starting audio if no user input has yet been performed.
64593 - Fix a bug where thread handles are not being freed.
64594 - Fix some static analysis warnings in FLAC, WAV and MP3 decoders.
64595 - Fix a warning due to referencing _MSC_VER when it is undefined.
64596 - Update to latest version of c89atomic.
64597 - Internal refactoring to migrate over to the new backend callback system for the following backends:
64607 v0.10.31 - 2021-01-17
64608 - Make some functions const correct.
64609 - Update ma_data_source_read_pcm_frames() to initialize pFramesRead to 0 for safety.
64610 - Add the MA_ATOMIC annotation for use with variables that should be used atomically and remove unnecessary volatile qualifiers.
64611 - Add support for enabling only specific backends at compile time. This is the reverse of the pre-existing system. With the new
64612 system, all backends are first disabled with `MA_ENABLE_ONLY_SPECIFIC_BACKENDS`, which is then followed with `MA_ENABLE_*`. The
64613 old system where you disable backends with `MA_NO_*` still exists and is still the default.
64615 v0.10.30 - 2021-01-10
64616 - Fix a crash in ma_audio_buffer_read_pcm_frames().
64617 - Update spinlock APIs to take a volatile parameter as input.
64618 - Silence some unused parameter warnings.
64619 - Fix a warning on GCC when compiling as C++.
64621 v0.10.29 - 2020-12-26
64622 - Fix some subtle multi-threading bugs on non-x86 platforms.
64623 - Fix a bug resulting in superfluous memory allocations when enumerating devices.
64624 - Core Audio: Fix a compilation error when compiling for iOS.
64626 v0.10.28 - 2020-12-16
64627 - Fix a crash when initializing a POSIX thread.
64628 - OpenSL|ES: Respect the MA_NO_RUNTIME_LINKING option.
64630 v0.10.27 - 2020-12-04
64631 - Add support for dynamically configuring some properties of `ma_noise` objects post-initialization.
64632 - Add support for configuring the channel mixing mode in the device config.
64633 - Fix a bug with simple channel mixing mode (drop or silence excess channels).
64634 - Fix some bugs with trying to access uninitialized variables.
64635 - Fix some errors with stopping devices for synchronous backends where the backend's stop callback would get fired twice.
64636 - Fix a bug in the decoder due to using an uninitialized variable.
64637 - Fix some data race errors.
64639 v0.10.26 - 2020-11-24
64640 - WASAPI: Fix a bug where the exclusive mode format may not be retrieved correctly due to accessing freed memory.
64641 - Fix a bug with ma_waveform where glitching occurs after changing frequency.
64642 - Fix compilation with OpenWatcom.
64643 - Fix compilation with TCC.
64644 - Fix compilation with Digital Mars.
64645 - Fix compilation warnings.
64646 - Remove bitfields from public structures to aid in binding maintenance.
64648 v0.10.25 - 2020-11-15
64649 - PulseAudio: Fix a bug where the stop callback isn't fired.
64650 - WebAudio: Fix an error that occurs when Emscripten increases the size of it's heap.
64651 - Custom Backends: Change the onContextInit and onDeviceInit callbacks to take a parameter which is a pointer to the config that was
64652 passed into ma_context_init() and ma_device_init(). This replaces the deviceType parameter of onDeviceInit.
64653 - Fix compilation warnings on older versions of GCC.
64655 v0.10.24 - 2020-11-10
64656 - 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
64657 lower priority backend.
64659 v0.10.23 - 2020-11-09
64660 - AAudio: Add support for configuring a playback stream's usage.
64661 - Fix a compilation error when all built-in asynchronous backends are disabled at compile time.
64662 - Fix compilation errors when compiling as C++.
64664 v0.10.22 - 2020-11-08
64665 - Add support for custom backends.
64666 - Add support for detecting default devices during device enumeration and with `ma_context_get_device_info()`.
64667 - Refactor to the PulseAudio backend. This simplifies the implementation and fixes a capture bug.
64668 - ALSA: Fix a bug in `ma_context_get_device_info()` where the PCM handle is left open in the event of an error.
64669 - Core Audio: Further improvements to sample rate selection.
64670 - Core Audio: Fix some bugs with capture mode.
64671 - OpenSL: Add support for configuring stream types and recording presets.
64672 - AAudio: Add support for configuring content types and input presets.
64673 - Fix bugs in `ma_decoder_init_file*()` where the file handle is not closed after a decoding error.
64674 - Fix some compilation warnings on GCC and Clang relating to the Speex resampler.
64675 - Fix a compilation error for the Linux build when the ALSA and JACK backends are both disabled.
64676 - Fix a compilation error for the BSD build.
64677 - Fix some compilation errors on older versions of GCC.
64678 - Add documentation for `MA_NO_RUNTIME_LINKING`.
64680 v0.10.21 - 2020-10-30
64681 - Add ma_is_backend_enabled() and ma_get_enabled_backends() for retrieving enabled backends at run-time.
64682 - WASAPI: Fix a copy and paste bug relating to loopback mode.
64683 - Core Audio: Fix a bug when using multiple contexts.
64684 - Core Audio: Fix a compilation warning.
64685 - Core Audio: Improvements to sample rate selection.
64686 - Core Audio: Improvements to format/channels/rate selection when requesting defaults.
64687 - Core Audio: Add notes regarding the Apple notarization process.
64688 - Fix some bugs due to null pointer dereferences.
64690 v0.10.20 - 2020-10-06
64691 - Fix build errors with UWP.
64692 - Minor documentation updates.
64694 v0.10.19 - 2020-09-22
64695 - WASAPI: Return an error when exclusive mode is requested, but the native format is not supported by miniaudio.
64696 - Fix a bug where ma_decoder_seek_to_pcm_frames() never returns MA_SUCCESS even though it was successful.
64697 - Store the sample rate in the `ma_lpf` and `ma_hpf` structures.
64699 v0.10.18 - 2020-08-30
64700 - Fix build errors with VC6.
64701 - Fix a bug in channel converter for s32 format.
64702 - 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
64703 config. This fixes an issue where the optimized mono expansion path would never get used.
64704 - Use a more appropriate default format for FLAC decoders. This will now use ma_format_s16 when the FLAC is encoded as 16-bit.
64705 - Update FLAC decoder.
64706 - Update links to point to the new repository location (https://github.com/mackron/miniaudio).
64708 v0.10.17 - 2020-08-28
64709 - Fix an error where the WAV codec is incorrectly excluded from the build depending on which compile time options are set.
64710 - Fix a bug in ma_audio_buffer_read_pcm_frames() where it isn't returning the correct number of frames processed.
64711 - Fix compilation error on Android.
64712 - Core Audio: Fix a bug with full-duplex mode.
64713 - Add ma_decoder_get_cursor_in_pcm_frames().
64714 - Update WAV codec.
64716 v0.10.16 - 2020-08-14
64717 - WASAPI: Fix a potential crash due to using an uninitialized variable.
64718 - OpenSL: Enable runtime linking.
64719 - OpenSL: Fix a multithreading bug when initializing and uninitializing multiple contexts at the same time.
64720 - iOS: Improvements to device enumeration.
64721 - Fix a crash in ma_data_source_read_pcm_frames() when the output frame count parameter is NULL.
64722 - Fix a bug in ma_data_source_read_pcm_frames() where looping doesn't work.
64723 - Fix some compilation warnings on Windows when both DirectSound and WinMM are disabled.
64724 - Fix some compilation warnings when no decoders are enabled.
64725 - Add ma_audio_buffer_get_available_frames().
64726 - Add ma_decoder_get_available_frames().
64727 - Add sample rate to ma_data_source_get_data_format().
64728 - Change volume APIs to take 64-bit frame counts.
64729 - Updates to documentation.
64731 v0.10.15 - 2020-07-15
64732 - Fix a bug when converting bit-masked channel maps to miniaudio channel maps. This affects the WASAPI and OpenSL backends.
64734 v0.10.14 - 2020-07-14
64735 - Fix compilation errors on Android.
64736 - Fix compilation errors with -march=armv6.
64737 - Updates to the documentation.
64739 v0.10.13 - 2020-07-11
64740 - Fix some potential buffer overflow errors with channel maps when channel counts are greater than MA_MAX_CHANNELS.
64741 - Fix compilation error on Emscripten.
64742 - Silence some unused function warnings.
64743 - Increase the default buffer size on the Web Audio backend. This fixes glitching issues on some browsers.
64744 - Bring FLAC decoder up-to-date with dr_flac.
64745 - Bring MP3 decoder up-to-date with dr_mp3.
64747 v0.10.12 - 2020-07-04
64748 - Fix compilation errors on the iOS build.
64750 v0.10.11 - 2020-06-28
64751 - Fix some bugs with device tracking on Core Audio.
64752 - Updates to documentation.
64754 v0.10.10 - 2020-06-26
64755 - Add include guard for the implementation section.
64756 - Mark ma_device_sink_info_callback() as static.
64757 - Fix compilation errors with MA_NO_DECODING and MA_NO_ENCODING.
64758 - Fix compilation errors with MA_NO_DEVICE_IO
64760 v0.10.9 - 2020-06-24
64761 - 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
64762 longer required. Decoding of WAV, FLAC and MP3 should be supported seamlessly without any additional libraries. Decoders can be excluded from the build
64763 with the following options:
64767 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
64768 the implementation of miniaudio, or update dr_wav, dr_flac and/or dr_mp3.
64769 - Changes to the internal atomics library. This has been replaced with c89atomic.h which is embedded within this file.
64770 - Fix a bug when a decoding backend reports configurations outside the limits of miniaudio's decoder abstraction.
64771 - Fix the UWP build.
64772 - Fix the Core Audio build.
64773 - Fix the -std=c89 build on GCC.
64775 v0.10.8 - 2020-06-22
64776 - Remove dependency on ma_context from mutexes.
64777 - Change ma_data_source_read_pcm_frames() to return a result code and output the frames read as an output parameter.
64778 - Change ma_data_source_seek_pcm_frames() to return a result code and output the frames seeked as an output parameter.
64779 - Change ma_audio_buffer_unmap() to return MA_AT_END when the end has been reached. This should be considered successful.
64780 - Change playback.pDeviceID and capture.pDeviceID to constant pointers in ma_device_config.
64781 - 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
64782 IO for the loading and reading of raw audio data. Passing in NULL for the VFS will use defaults. New APIs:
64783 - ma_decoder_init_vfs()
64784 - ma_decoder_init_vfs_wav()
64785 - ma_decoder_init_vfs_flac()
64786 - ma_decoder_init_vfs_mp3()
64787 - ma_decoder_init_vfs_vorbis()
64788 - ma_decoder_init_vfs_w()
64789 - ma_decoder_init_vfs_wav_w()
64790 - ma_decoder_init_vfs_flac_w()
64791 - ma_decoder_init_vfs_mp3_w()
64792 - ma_decoder_init_vfs_vorbis_w()
64793 - Add support for memory mapping to ma_data_source.
64794 - ma_data_source_map()
64795 - ma_data_source_unmap()
64796 - 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.
64797 - Add initial implementation of ma_yield() which is useful for spin locks which will be used in some upcoming work.
64798 - Add documentation for log levels.
64799 - The ma_event API has been made public in preparation for some uncoming work.
64800 - 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.
64801 - Fix some bugs with the linear resampler when dynamically changing the sample rate.
64802 - Fix compilation errors with MA_NO_DEVICE_IO.
64803 - Fix some warnings with GCC and -std=c89.
64804 - Fix some formatting warnings with GCC and -Wall and -Wpedantic.
64805 - Fix some warnings with VC6.
64806 - Minor optimization to ma_copy_pcm_frames(). This is now a no-op when the input and output buffers are the same.
64808 v0.10.7 - 2020-05-25
64809 - Fix a compilation error in the C++ build.
64810 - Silence a warning.
64812 v0.10.6 - 2020-05-24
64813 - Change ma_clip_samples_f32() and ma_clip_pcm_frames_f32() to take a 64-bit sample/frame count.
64814 - Change ma_zero_pcm_frames() to clear to 128 for ma_format_u8.
64815 - Add ma_silence_pcm_frames() which replaces ma_zero_pcm_frames(). ma_zero_pcm_frames() will be removed in version 0.11.
64816 - Add support for u8, s24 and s32 formats to ma_channel_converter.
64817 - Add compile-time and run-time version querying.
64820 - MA_VERSION_REVISION
64821 - MA_VERSION_STRING
64823 - ma_version_string()
64824 - Add ma_audio_buffer for reading raw audio data directly from memory.
64825 - Fix a bug in shuffle mode in ma_channel_converter.
64826 - Fix compilation errors in certain configurations for ALSA and PulseAudio.
64827 - The data callback now initializes the output buffer to 128 when the playback sample format is ma_format_u8.
64829 v0.10.5 - 2020-05-05
64830 - Change ma_zero_pcm_frames() to take a 64-bit frame count.
64831 - Add ma_copy_pcm_frames().
64832 - Add MA_NO_GENERATION build option to exclude the `ma_waveform` and `ma_noise` APIs from the build.
64833 - Add support for formatted logging to the VC6 build.
64834 - Fix a crash in the linear resampler when LPF order is 0.
64835 - Fix compilation errors and warnings with older versions of Visual Studio.
64836 - Minor documentation updates.
64838 v0.10.4 - 2020-04-12
64839 - Fix a data conversion bug when converting from the client format to the native device format.
64841 v0.10.3 - 2020-04-07
64842 - Bring up to date with breaking changes to dr_mp3.
64843 - Remove MA_NO_STDIO. This was causing compilation errors and the maintenance cost versus practical benefit is no longer worthwhile.
64844 - Fix a bug with data conversion where it was unnecessarily converting to s16 or f32 and then straight back to the original format.
64845 - Fix compilation errors and warnings with Visual Studio 2005.
64846 - ALSA: Disable ALSA's automatic data conversion by default and add configuration options to the device config:
64847 - alsa.noAutoFormat
64848 - alsa.noAutoChannels
64849 - alsa.noAutoResample
64850 - WASAPI: Add some overrun recovery for ma_device_type_capture devices.
64852 v0.10.2 - 2020-03-22
64853 - Decorate some APIs with MA_API which were missed in the previous version.
64854 - Fix a bug in ma_linear_resampler_set_rate() and ma_linear_resampler_set_rate_ratio().
64856 v0.10.1 - 2020-03-17
64857 - Add MA_API decoration. This can be customized by defining it before including miniaudio.h.
64858 - Fix a bug where opening a file would return a success code when in fact it failed.
64859 - Fix compilation errors with Visual Studio 6 and 2003.
64860 - Fix warnings on macOS.
64862 v0.10.0 - 2020-03-07
64863 - API CHANGE: Refactor data conversion APIs
64864 - ma_format_converter has been removed. Use ma_convert_pcm_frames_format() instead.
64865 - ma_channel_router has been replaced with ma_channel_converter.
64866 - ma_src has been replaced with ma_resampler
64867 - ma_pcm_converter has been replaced with ma_data_converter
64868 - API CHANGE: Add support for custom memory allocation callbacks. The following APIs have been updated to take an extra parameter for the allocation
64873 - ma_aligned_malloc()
64874 - ma_aligned_free()
64875 - ma_rb_init() / ma_rb_init_ex()
64876 - ma_pcm_rb_init() / ma_pcm_rb_init_ex()
64877 - API CHANGE: Simplify latency specification in device configurations. The bufferSizeInFrames and bufferSizeInMilliseconds parameters have been replaced with
64878 periodSizeInFrames and periodSizeInMilliseconds respectively. The previous variables defined the size of the entire buffer, whereas the new ones define the
64879 size of a period. The following APIs have been removed since they are no longer relevant:
64880 - ma_get_default_buffer_size_in_milliseconds()
64881 - ma_get_default_buffer_size_in_frames()
64882 - 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
64884 - API CHANGE: The ma_sine_wave API has been replaced with ma_waveform. The following APIs have been removed:
64885 - ma_sine_wave_init()
64886 - ma_sine_wave_read_f32()
64887 - ma_sine_wave_read_f32_ex()
64888 - 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
64890 - 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
64892 - API CHANGE: ma_calculate_frame_count_after_src() has been renamed to ma_calculate_frame_count_after_resampling().
64893 - Add support for the following filters:
64894 - Biquad (ma_biquad)
64895 - First order low-pass (ma_lpf1)
64896 - Second order low-pass (ma_lpf2)
64897 - Low-pass with configurable order (ma_lpf)
64898 - First order high-pass (ma_hpf1)
64899 - Second order high-pass (ma_hpf2)
64900 - High-pass with configurable order (ma_hpf)
64901 - Second order band-pass (ma_bpf2)
64902 - Band-pass with configurable order (ma_bpf)
64903 - Second order peaking EQ (ma_peak2)
64904 - Second order notching (ma_notch2)
64905 - Second order low shelf (ma_loshelf2)
64906 - Second order high shelf (ma_hishelf2)
64907 - Add waveform generation API (ma_waveform) with support for the following:
64912 - Add noise generation API (ma_noise) with support for the following:
64916 - Add encoding API (ma_encoder). This only supports outputting to WAV files via dr_wav.
64917 - Add ma_result_description() which is used to retrieve a human readable description of a given result code.
64918 - Result codes have been changed. Binding maintainers will need to update their result code constants.
64919 - More meaningful result codes are now returned when a file fails to open.
64920 - Internal functions have all been made static where possible.
64921 - Fix potential crash when ma_device object's are not aligned to MA_SIMD_ALIGNMENT.
64922 - 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.
64923 - Fix bugs in some backends where the device is not drained properly in ma_device_stop().
64924 - Improvements to documentation.
64926 v0.9.10 - 2020-01-15
64927 - Fix compilation errors due to #if/#endif mismatches.
64928 - WASAPI: Fix a bug where automatic stream routing is being performed for devices that are initialized with an explicit device ID.
64929 - iOS: Fix a crash on device uninitialization.
64931 v0.9.9 - 2020-01-09
64932 - Fix compilation errors with MinGW.
64933 - Fix compilation errors when compiling on Apple platforms.
64934 - WASAPI: Add support for disabling hardware offloading.
64935 - WASAPI: Add support for disabling automatic stream routing.
64936 - Core Audio: Fix bugs in the case where the internal device uses deinterleaved buffers.
64937 - Core Audio: Add support for controlling the session category (AVAudioSessionCategory) and options (AVAudioSessionCategoryOptions).
64938 - JACK: Fix bug where incorrect ports are connected.
64940 v0.9.8 - 2019-10-07
64941 - WASAPI: Fix a potential deadlock when starting a full-duplex device.
64942 - WASAPI: Enable automatic resampling by default. Disable with config.wasapi.noAutoConvertSRC.
64943 - Core Audio: Fix bugs with automatic stream routing.
64944 - Add support for controlling whether or not the content of the output buffer passed in to the data callback is pre-initialized
64945 to zero. By default it will be initialized to zero, but this can be changed by setting noPreZeroedOutputBuffer in the device
64946 config. Setting noPreZeroedOutputBuffer to true will leave the contents undefined.
64947 - Add support for clipping samples after the data callback has returned. This only applies when the playback sample format is
64948 configured as ma_format_f32. If you are doing clipping yourself, you can disable this overhead by setting noClip to true in
64950 - Add support for master volume control for devices.
64951 - 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.
64952 - Use ma_device_set_master_gain_db() to set the volume in decibels where 0 is full volume and < 0 reduces the volume.
64953 - Fix warnings emitted by GCC when `__inline__` is undefined or defined as nothing.
64955 v0.9.7 - 2019-08-28
64956 - Add support for loopback mode (WASAPI only).
64957 - To use this, set the device type to ma_device_type_loopback, and then fill out the capture section of the device config.
64958 - If you need to capture from a specific output device, set the capture device ID to that of a playback device.
64959 - Fix a crash when an error is posted in ma_device_init().
64960 - Fix a compilation error when compiling for ARM architectures.
64961 - Fix a bug with the audio(4) backend where the device is incorrectly being opened in non-blocking mode.
64962 - Fix memory leaks in the Core Audio backend.
64963 - Minor refactoring to the WinMM, ALSA, PulseAudio, OSS, audio(4), sndio and null backends.
64965 v0.9.6 - 2019-08-04
64966 - Add support for loading decoders using a wchar_t string for file paths.
64967 - Don't trigger an assert when ma_device_start() is called on a device that is already started. This will now log a warning
64968 and return MA_INVALID_OPERATION. The same applies for ma_device_stop().
64969 - Try fixing an issue with PulseAudio taking a long time to start playback.
64970 - Fix a bug in ma_convert_frames() and ma_convert_frames_ex().
64971 - Fix memory leaks in the WASAPI backend.
64972 - Fix a compilation error with Visual Studio 2010.
64974 v0.9.5 - 2019-05-21
64975 - Add logging to ma_dlopen() and ma_dlsym().
64976 - Add ma_decoder_get_length_in_pcm_frames().
64977 - Fix a bug with capture on the OpenSL|ES backend.
64978 - Fix a bug with the ALSA backend where a device would not restart after being stopped.
64980 v0.9.4 - 2019-05-06
64981 - Add support for C89. With this change, miniaudio should compile clean with GCC/Clang with "-std=c89 -ansi -pedantic" and
64982 Microsoft compilers back to VC6. Other compilers should also work, but have not been tested.
64984 v0.9.3 - 2019-04-19
64985 - Fix compiler errors on GCC when compiling with -std=c99.
64987 v0.9.2 - 2019-04-08
64988 - Add support for per-context user data.
64989 - Fix a potential bug with context configs.
64990 - Fix some bugs with PulseAudio.
64992 v0.9.1 - 2019-03-17
64993 - Fix a bug where the output buffer is not getting zeroed out before calling the data callback. This happens when
64994 the device is running in passthrough mode (not doing any data conversion).
64995 - Fix an issue where the data callback is getting called too frequently on the WASAPI and DirectSound backends.
64996 - Fix error on the UWP build.
64997 - Fix a build error on Apple platforms.
65000 - Rebranded to "miniaudio". All namespaces have been renamed from "mal" to "ma".
65001 - API CHANGE: ma_device_init() and ma_device_config_init() have changed significantly:
65002 - The device type, device ID and user data pointer have moved from ma_device_init() to the config.
65003 - All variations of ma_device_config_init_*() have been removed in favor of just ma_device_config_init().
65004 - ma_device_config_init() now takes only one parameter which is the device type. All other properties need
65005 to be set on the returned object directly.
65006 - The onDataCallback and onStopCallback members of ma_device_config have been renamed to "dataCallback"
65007 and "stopCallback".
65008 - The ID of the physical device is now split into two: one for the playback device and the other for the
65009 capture device. This is required for full-duplex. These are named "pPlaybackDeviceID" and "pCaptureDeviceID".
65010 - API CHANGE: The data callback has changed. It now uses a unified callback for all device types rather than
65011 being separate for each. It now takes two pointers - one containing input data and the other output data. This
65012 design in required for full-duplex. The return value is now void instead of the number of frames written. The
65013 new callback looks like the following:
65014 void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
65015 - API CHANGE: Remove the log callback parameter from ma_context_config_init(). With this change,
65016 ma_context_config_init() now takes no parameters and the log callback is set via the structure directly. The
65017 new policy for config initialization is that only mandatory settings are passed in to *_config_init(). The
65018 "onLog" member of ma_context_config has been renamed to "logCallback".
65019 - API CHANGE: Remove ma_device_get_buffer_size_in_bytes().
65020 - API CHANGE: Rename decoding APIs to "pcm_frames" convention.
65021 - mal_decoder_read() -> ma_decoder_read_pcm_frames()
65022 - mal_decoder_seek_to_frame() -> ma_decoder_seek_to_pcm_frame()
65023 - API CHANGE: Rename sine wave reading APIs to f32 convention.
65024 - mal_sine_wave_read() -> ma_sine_wave_read_f32()
65025 - mal_sine_wave_read_ex() -> ma_sine_wave_read_f32_ex()
65026 - API CHANGE: Remove some deprecated APIs
65027 - mal_device_set_recv_callback()
65028 - mal_device_set_send_callback()
65029 - mal_src_set_input_sample_rate()
65030 - mal_src_set_output_sample_rate()
65031 - API CHANGE: Add log level to the log callback. New signature:
65032 - void on_log(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
65033 - API CHANGE: Changes to result codes. Constants have changed and unused codes have been removed. If you're
65034 a binding mainainer you will need to update your result code constants.
65035 - API CHANGE: Change the order of the ma_backend enums to priority order. If you are a binding maintainer, you
65036 will need to update.
65037 - API CHANGE: Rename mal_dsp to ma_pcm_converter. All functions have been renamed from mal_dsp_*() to
65038 ma_pcm_converter_*(). All structures have been renamed from mal_dsp* to ma_pcm_converter*.
65039 - API CHANGE: Reorder parameters of ma_decoder_read_pcm_frames() to be consistent with the new parameter order scheme.
65040 - The resampling algorithm has been changed from sinc to linear. The rationale for this is that the sinc implementation
65041 is too inefficient right now. This will hopefully be improved at a later date.
65042 - Device initialization will no longer fall back to shared mode if exclusive mode is requested but is unusable.
65043 With this change, if you request an device in exclusive mode, but exclusive mode is not supported, it will not
65044 automatically fall back to shared mode. The client will need to reinitialize the device in shared mode if that's
65046 - Add ring buffer API. This is ma_rb and ma_pcm_rb, the difference being that ma_rb operates on bytes and
65047 ma_pcm_rb operates on PCM frames.
65048 - Add Web Audio backend. This is used when compiling with Emscripten. The SDL backend, which was previously
65049 used for web support, will be removed in a future version.
65050 - Add AAudio backend (Android Audio). This is the new priority backend for Android. Support for AAudio starts
65051 with Android 8. OpenSL|ES is used as a fallback for older versions of Android.
65052 - Remove OpenAL and SDL backends.
65053 - Fix a possible deadlock when rapidly stopping the device after it has started.
65054 - Update documentation.
65055 - Change licensing to a choice of public domain _or_ MIT-0 (No Attribution).
65057 v0.8.14 - 2018-12-16
65058 - Core Audio: Fix a bug where the device state is not set correctly after stopping.
65059 - Add support for custom weights to the channel router.
65060 - Update decoders to use updated APIs in dr_flac, dr_mp3 and dr_wav.
65062 v0.8.13 - 2018-12-04
65063 - Core Audio: Fix a bug with channel mapping.
65064 - Fix a bug with channel routing where the back/left and back/right channels have the wrong weight.
65066 v0.8.12 - 2018-11-27
65067 - Drop support for SDL 1.2. The Emscripten build now requires "-s USE_SDL=2".
65068 - Fix a linking error with ALSA.
65069 - Fix a bug on iOS where the device name is not set correctly.
65071 v0.8.11 - 2018-11-21
65073 - Minor tweaks to PulseAudio.
65075 v0.8.10 - 2018-10-21
65076 - Core Audio: Fix a hang when uninitializing a device.
65077 - Fix a bug where an incorrect value is returned from mal_device_stop().
65079 v0.8.9 - 2018-09-28
65080 - Fix a bug with the SDL backend where device initialization fails.
65082 v0.8.8 - 2018-09-14
65083 - Fix Linux build with the ALSA backend.
65084 - Minor documentation fix.
65086 v0.8.7 - 2018-09-12
65087 - Fix a bug with UWP detection.
65089 v0.8.6 - 2018-08-26
65090 - Automatically switch the internal device when the default device is unplugged. Note that this is still in the
65091 early stages and not all backends handle this the same way. As of this version, this will not detect a default
65092 device switch when changed from the operating system's audio preferences (unless the backend itself handles
65093 this automatically). This is not supported in exclusive mode.
65094 - WASAPI and Core Audio: Add support for stream routing. When the application is using a default device and the
65095 user switches the default device via the operating system's audio preferences, miniaudio will automatically switch
65096 the internal device to the new default. This is not supported in exclusive mode.
65097 - WASAPI: Add support for hardware offloading via IAudioClient2. Only supported on Windows 8 and newer.
65098 - WASAPI: Add support for low-latency shared mode via IAudioClient3. Only supported on Windows 10 and newer.
65099 - Add support for compiling the UWP build as C.
65100 - mal_device_set_recv_callback() and mal_device_set_send_callback() have been deprecated. You must now set this
65101 when the device is initialized with mal_device_init*(). These will be removed in version 0.9.0.
65103 v0.8.5 - 2018-08-12
65104 - Add support for specifying the size of a device's buffer in milliseconds. You can still set the buffer size in
65105 frames if that suits you. When bufferSizeInFrames is 0, bufferSizeInMilliseconds will be used. If both are non-0
65106 then bufferSizeInFrames will take priority. If both are set to 0 the default buffer size is used.
65107 - Add support for the audio(4) backend to OpenBSD.
65108 - Fix a bug with the ALSA backend that was causing problems on Raspberry Pi. This significantly improves the
65109 Raspberry Pi experience.
65110 - Fix a bug where an incorrect number of samples is returned from sinc resampling.
65111 - Add support for setting the value to be passed to internal calls to CoInitializeEx().
65112 - WASAPI and WinMM: Stop the device when it is unplugged.
65114 v0.8.4 - 2018-08-06
65115 - Add sndio backend for OpenBSD.
65116 - Add audio(4) backend for NetBSD.
65117 - Drop support for the OSS backend on everything except FreeBSD and DragonFly BSD.
65118 - Formats are now native-endian (were previously little-endian).
65119 - Mark some APIs as deprecated:
65120 - mal_src_set_input_sample_rate() and mal_src_set_output_sample_rate() are replaced with mal_src_set_sample_rate().
65121 - mal_dsp_set_input_sample_rate() and mal_dsp_set_output_sample_rate() are replaced with mal_dsp_set_sample_rate().
65122 - Fix a bug when capturing using the WASAPI backend.
65123 - Fix some aliasing issues with resampling, specifically when increasing the sample rate.
65126 v0.8.3 - 2018-07-15
65127 - Fix a crackling bug when resampling in capture mode.
65128 - Core Audio: Fix a bug where capture does not work.
65129 - ALSA: Fix a bug where the worker thread can get stuck in an infinite loop.
65130 - PulseAudio: Fix a bug where mal_context_init() succeeds when PulseAudio is unusable.
65131 - JACK: Fix a bug where mal_context_init() succeeds when JACK is unusable.
65133 v0.8.2 - 2018-07-07
65134 - Fix a bug on macOS with Core Audio where the internal callback is not called.
65136 v0.8.1 - 2018-07-06
65137 - Fix compilation errors and warnings.
65140 - Changed MAL_IMPLEMENTATION to MINI_AL_IMPLEMENTATION for consistency with other libraries. The old
65141 way is still supported for now, but you should update as it may be removed in the future.
65142 - API CHANGE: Replace device enumeration APIs. mal_enumerate_devices() has been replaced with
65143 mal_context_get_devices(). An additional low-level device enumration API has been introduced called
65144 mal_context_enumerate_devices() which uses a callback to report devices.
65145 - API CHANGE: Rename mal_get_sample_size_in_bytes() to mal_get_bytes_per_sample() and add
65146 mal_get_bytes_per_frame().
65147 - API CHANGE: Replace mal_device_config.preferExclusiveMode with mal_device_config.shareMode.
65148 - This new config can be set to mal_share_mode_shared (default) or mal_share_mode_exclusive.
65149 - API CHANGE: Remove excludeNullDevice from mal_context_config.alsa.
65150 - API CHANGE: Rename MAL_MAX_SAMPLE_SIZE_IN_BYTES to MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES.
65151 - API CHANGE: Change the default channel mapping to the standard Microsoft mapping.
65152 - API CHANGE: Remove backend-specific result codes.
65153 - API CHANGE: Changes to the format conversion APIs (mal_pcm_f32_to_s16(), etc.)
65154 - Add support for Core Audio (Apple).
65155 - Add support for PulseAudio.
65156 - This is the highest priority backend on Linux (higher priority than ALSA) since it is commonly
65157 installed by default on many of the popular distros and offer's more seamless integration on
65158 platforms where PulseAudio is used. In addition, if PulseAudio is installed and running (which
65159 is extremely common), it's better to just use PulseAudio directly rather than going through the
65160 "pulse" ALSA plugin (which is what the "default" ALSA device is likely set to).
65161 - Add support for JACK.
65162 - Remove dependency on asound.h for the ALSA backend. This means the ALSA development packages are no
65163 longer required to build miniaudio.
65164 - Remove dependency on dsound.h for the DirectSound backend. This fixes build issues with some
65165 distributions of MinGW.
65166 - Remove dependency on audioclient.h for the WASAPI backend. This fixes build issues with some
65167 distributions of MinGW.
65168 - Add support for dithering to format conversion.
65169 - Add support for configuring the priority of the worker thread.
65170 - Add a sine wave generator.
65171 - Improve efficiency of sample rate conversion.
65172 - Introduce the notion of standard channel maps. Use mal_get_standard_channel_map().
65173 - Introduce the notion of default device configurations. A default config uses the same configuration
65174 as the backend's internal device, and as such results in a pass-through data transmission pipeline.
65175 - Add support for passing in NULL for the device config in mal_device_init(), which uses a default
65176 config. This requires manually calling mal_device_set_send/recv_callback().
65177 - Add support for decoding from raw PCM data (mal_decoder_init_raw(), etc.)
65178 - Make mal_device_init_ex() more robust.
65179 - Make some APIs more const-correct.
65180 - Fix errors with SDL detection on Apple platforms.
65181 - Fix errors with OpenAL detection.
65182 - Fix some memory leaks.
65183 - Fix a bug with opening decoders from memory.
65184 - Early work on SSE2, AVX2 and NEON optimizations.
65185 - Miscellaneous bug fixes.
65186 - Documentation updates.
65189 - API CHANGE: Change mal_src_read_frames() and mal_dsp_read_frames() to use 64-bit sample counts.
65190 - Add decoder APIs for loading WAV, FLAC, Vorbis and MP3 files.
65191 - Allow opening of devices without a context.
65192 - In this case the context is created and managed internally by the device.
65193 - Change the default channel mapping to the same as that used by FLAC.
65194 - Fix build errors with macOS.
65197 - Fix build errors with BSD/OSS.
65200 - Fix some warnings when compiling with Visual C++.
65203 - Fix errors with channel mixing when increasing the channel count.
65204 - Improvements to the build system for the OpenAL backend.
65205 - Documentation fixes.
65208 - API CHANGE: Expose and improve mutex APIs. If you were using the mutex APIs before this version you'll
65210 - API CHANGE: SRC and DSP callbacks now take a pointer to a mal_src and mal_dsp object respectively.
65211 - API CHANGE: Improvements to event and thread APIs. These changes make these APIs more consistent.
65212 - Add support for SDL and Emscripten.
65213 - Simplify the build system further for when development packages for various backends are not installed.
65214 With this change, when the compiler supports __has_include, backends without the relevant development
65215 packages installed will be ignored. This fixes the build for old versions of MinGW.
65216 - Fixes to the Android build.
65217 - Add mal_convert_frames(). This is a high-level helper API for performing a one-time, bulk conversion of
65218 audio data to a different format.
65219 - Improvements to f32 -> u8/s16/s24/s32 conversion routines.
65220 - Fix a bug where the wrong value is returned from mal_device_start() for the OpenSL backend.
65221 - Fixes and improvements for Raspberry Pi.
65225 - API CHANGE: The mal_context_init() function now takes a pointer to a mal_context_config object for
65226 configuring the context. The works in the same kind of way as the device config. The rationale for this
65227 change is to give applications better control over context-level properties, add support for backend-
65228 specific configurations, and support extensibility without breaking the API.
65229 - API CHANGE: The alsa.preferPlugHW device config variable has been removed since it's not really useful for
65231 - ALSA: By default, device enumeration will now only enumerate over unique card/device pairs. Applications
65232 can enable verbose device enumeration by setting the alsa.useVerboseDeviceEnumeration context config
65234 - ALSA: When opening a device in shared mode (the default), the dmix/dsnoop plugin will be prioritized. If
65235 this fails it will fall back to the hw plugin. With this change the preferExclusiveMode config is now
65236 honored. Note that this does not happen when alsa.useVerboseDeviceEnumeration is set to true (see above)
65237 which is by design.
65238 - ALSA: Add support for excluding the "null" device using the alsa.excludeNullDevice context config variable.
65239 - ALSA: Fix a bug with channel mapping which causes an assertion to fail.
65240 - Fix errors with enumeration when pInfo is set to NULL.
65241 - OSS: Fix a bug when starting a device when the client sends 0 samples for the initial buffer fill.
65244 - API CHANGE: The log callback is now per-context rather than per-device and as is thus now passed to
65245 mal_context_init(). The rationale for this change is that it allows applications to capture diagnostic
65246 messages at the context level. Previously this was only available at the device level.
65247 - API CHANGE: The device config passed to mal_device_init() is now const.
65248 - Added support for OSS which enables support on BSD platforms.
65249 - Added support for WinMM (waveOut/waveIn).
65250 - Added support for UWP (Universal Windows Platform) applications. Currently C++ only.
65251 - Added support for exclusive mode for selected backends. Currently supported on WASAPI.
65252 - POSIX builds no longer require explicit linking to libpthread (-lpthread).
65253 - ALSA: Explicit linking to libasound (-lasound) is no longer required.
65254 - ALSA: Latency improvements.
65255 - ALSA: Use MMAP mode where available. This can be disabled with the alsa.noMMap config.
65256 - ALSA: Use "hw" devices instead of "plughw" devices by default. This can be disabled with the
65257 alsa.preferPlugHW config.
65258 - WASAPI is now the highest priority backend on Windows platforms.
65259 - Fixed an error with sample rate conversion which was causing crackling when capturing.
65260 - Improved error handling.
65261 - Improved compiler support.
65262 - Miscellaneous bug fixes.
65265 - API CHANGE: Introduced the notion of a context. The context is the highest level object and is required for
65266 enumerating and creating devices. Now, applications must first create a context, and then use that to
65267 enumerate and create devices. The reason for this change is to ensure device enumeration and creation is
65268 tied to the same backend. In addition, some backends are better suited to this design.
65269 - API CHANGE: Removed the rewinding APIs because they're too inconsistent across the different backends, hard
65270 to test and maintain, and just generally unreliable.
65271 - Added helper APIs for initializing mal_device_config objects.
65272 - Null Backend: Fixed a crash when recording.
65273 - Fixed build for UWP.
65274 - Added support for f32 formats to the OpenSL|ES backend.
65275 - Added initial implementation of the WASAPI backend.
65276 - Added initial implementation of the OpenAL backend.
65277 - Added support for low quality linear sample rate conversion.
65278 - Added early support for basic channel mapping.
65281 - API CHANGE: Add user data pointer as the last parameter to mal_device_init(). The rationale for this
65282 change is to ensure the logging callback has access to the user data during initialization.
65283 - API CHANGE: Have device configuration properties be passed to mal_device_init() via a structure. Rationale:
65284 1) The number of parameters is just getting too much.
65285 2) It makes it a bit easier to add new configuration properties in the future. In particular, there's a
65286 chance there will be support added for backend-specific properties.
65287 - Dropped support for f64, A-law and Mu-law formats since they just aren't common enough to justify the
65288 added maintenance cost.
65289 - DirectSound: Increased the default buffer size for capture devices.
65290 - Added initial implementation of the OpenSL|ES backend.
65293 - Initial versioned release.
65298 This software is available as a choice of the following licenses. Choose
65299 whichever you prefer.
65301 ===============================================================================
65302 ALTERNATIVE 1 - Public Domain (www.unlicense.org)
65303 ===============================================================================
65304 This is free and unencumbered software released into the public domain.
65306 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
65307 software, either in source code form or as a compiled binary, for any purpose,
65308 commercial or non-commercial, and by any means.
65310 In jurisdictions that recognize copyright laws, the author or authors of this
65311 software dedicate any and all copyright interest in the software to the public
65312 domain. We make this dedication for the benefit of the public at large and to
65313 the detriment of our heirs and successors. We intend this dedication to be an
65314 overt act of relinquishment in perpetuity of all present and future rights to
65315 this software under copyright law.
65317 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
65318 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
65319 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
65320 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
65321 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
65322 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
65324 For more information, please refer to <http://unlicense.org/>
65326 ===============================================================================
65327 ALTERNATIVE 2 - MIT No Attribution
65328 ===============================================================================
65329 Copyright 2020 David Reid
65331 Permission is hereby granted, free of charge, to any person obtaining a copy of
65332 this software and associated documentation files (the "Software"), to deal in
65333 the Software without restriction, including without limitation the rights to
65334 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
65335 of the Software, and to permit persons to whom the Software is furnished to do
65338 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
65339 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
65340 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
65341 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
65342 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
65343 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE