1/*
2Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
3miniaudio (formerly mini_al) - v0.xx.xx - 2020-xx-xx
4
5David Reid - davidreidsoftware@gmail.com
6
7https://github.com/dr-soft/miniaudio
8*/
9
10/*
11RELEASE NOTES - VERSION 0.10
12============================
13Version 0.10 includes major API changes and refactoring, mostly concerned with the data conversion system. Data conversion is performed internally to convert
14audio 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
15to the `ma_decoder` object. The previous design has several design flaws and missing features which necessitated a complete redesign.
16
17
18Changes to Data Conversion
19--------------------------
20The previous data conversion system used callbacks to deliver input data for conversion. This design works well in some specific situations, but in other
21situations 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
22pointer to the input data directly rather than dealing with a callback.
23
24The following are the data conversion APIs that have been removed and their replacements:
25
26 - ma_format_converter -> ma_convert_pcm_frames_format()
27 - ma_channel_router -> ma_channel_converter
28 - ma_src -> ma_resampler
29 - ma_pcm_converter -> ma_data_converter
30
31The 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
32`*_process_pcm_frames()` function as a pointer to a buffer.
33
34The simplest aspect of data conversion is sample format conversion. To convert between two formats, just call `ma_convert_pcm_frames_format()`. Channel
35conversion is also simple which you can do with `ma_channel_router` via `ma_channel_router_process_pcm_frames().
36
37Resampling 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
38call `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
39output. Upon returning they will receive the number of input frames that were consumed and the number of output frames that were generated.
40
41The `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
42makes it the best option if you need to do data conversion.
43
44In addition to changes to the API design, a few other changes have been made to the data conversion pipeline:
45
46 - The sinc resampler has been removed. This was completely broken and never actually worked properly.
47 - The linear resampler can now uses low-pass filtering to remove aliasing. The quality of the low-pass filter can be controlled via the resampler config with
48 the `lpfCount` option, which has a maximum value of MA_MAX_RESAMPLER_LPF_FILTERS.
49 - Data conversion now supports s16 natively which runs through a fixed point pipeline. Previously everything needed to be converted to floating point before
50 processing, whereas now both s16 and f32 are natively supported. Other formats still require conversion to either s16 or f32 prior to processing, however
51 `ma_data_converter` will handle this for you.
52
53
54Custom Memory Allocators
55------------------------
56miniaudio has always supported macro level customization for memory allocation via MA_MALLOC, MA_REALLOC and MA_FREE, however some scenarios require more
57flexibility 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
58`ma_allocation_callbacks` structure. Anything making use of heap allocations has been updated to accept this new structure.
59
60The `ma_context_config` structure has been updated with a new member called `allocationCallbacks`. Leaving this set to it's defaults returned by
61`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
62way, and leaving everything as-is after `ma_decoder_config_init()` will cause it to use the same defaults.
63
64The 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.
65Otherwise they will use the relevant callback in the structure.
66
67 - ma_malloc()
68 - ma_realloc()
69 - ma_free()
70 - ma_aligned_malloc()
71 - ma_aligned_free()
72 - ma_rb_init() / ma_rb_init_ex()
73 - ma_pcm_rb_init() / ma_pcm_rb_init_ex()
74
75Note 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
76allocation callbacks.
77
78
79Buffer and Period Configuration Changes
80---------------------------------------
81The 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
82`bufferSizeInFrames` and `bufferSizeInMilliseconds` defined the size of the entire buffer, with the size of a period being the size of this variable divided by
83the period count. This became confusing because people would expect the value of `bufferSizeInFrames` or `bufferSizeInMilliseconds` to independantly determine
84latency, 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
85`periodSizeInFrames` and `periodSizeInMilliseconds`.
86
87These 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
88that 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
89configure latency.
90
91The following unused APIs have been removed:
92
93 ma_get_default_buffer_size_in_milliseconds()
94 ma_get_default_buffer_size_in_frames()
95
96The following macros have been removed:
97
98 MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY
99 MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE
100
101
102Other API Changes
103-----------------
104Other less major API changes have also been made in version 0.10.
105
106`ma_device_set_stop_callback()` has been removed. You now must set the stop callback via the device config just like the data callback.
107
108The `ma_sine_wave` API has been replaced with a more general API called `ma_waveform`. This supports generation of different types of waveforms, including
109sine, square, triangle and sawtooth. Use `ma_waveform_init()` in place of `ma_sine_wave_init()` to initialize the waveform object. This takes the same
110parameters, except an additional `ma_waveform_type` value which you would set to `ma_waveform_type_sine`. Use `ma_waveform_read_pcm_frames()` in place of
111`ma_sine_wave_read_f32()` and `ma_sine_wave_read_f32_ex()`.
112
113`ma_convert_frames()` and `ma_convert_frames_ex()` have been changed. Both of these functions now take a new parameter called `frameCountOut` which specifies
114the 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
115take 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 make it more
116flexible, to prevent the parameter list getting too long, and to prevent API breakage whenever a new conversion property is added.
117
118`ma_calculate_frame_count_after_src()` has been renamed to `ma_calculate_frame_count_after_resampling()` for consistency with the new `ma_resampler` API.
119
120
121Biquad and Low-Pass Filters
122---------------------------
123A generic biquad filter has been added. This is used via the `ma_biquad` API. The biquad filter is used as the basis for the low-pass filter. The biquad filter
124supports 32-bit floating point samples which runs on a floating point pipeline and 16-bit signed integer samples which runs on a 32-bit fixed point pipeline.
125Both formats use transposed direct form 2.
126
127The low-pass filter is just a biquad filter. By itself it's a second order low-pass filter, but it can be extended to higher orders by chaining low-pass
128filters together. Low-pass filtering is achieved via the `ma_lpf` API. Since the low-pass filter is just a biquad filter, it supports both 32-bit floating
129point and 16-bit signed integer formats.
130
131
132Sine, Square, Triangle and Sawtooth Waveforms
133---------------------------------------------
134Previously miniaudio supported only sine wave generation. This has now been generalized to support sine, square, triangle and sawtooth waveforms. The old
135`ma_sine_wave` API has been removed and replaced with the `ma_waveform` API. Use `ma_waveform_init()` to initialize the waveform. Here you specify tyhe type of
136waveform you want to generated. You then read data using `ma_waveform_read_pcm_frames()`.
137
138
139Miscellaneous Changes
140---------------------
141Internal functions have all been made static where possible. If you get warnings about unused functions, please submit a bug report.
142
143The `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
144the 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
145was 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,
146this has been removed from all structures.
147*/
148
149
150/*
151Introduction
152============
153miniaudio is a single file library for audio playback and capture. To use it, do the following in one .c file:
154
155 ```c
156 #define MINIAUDIO_IMPLEMENTATION
157 #include "miniaudio.h
158 ```
159
160You can #include miniaudio.h in other parts of the program just like any other header.
161
162miniaudio 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,
163and 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
164initializing the device.
165
166When 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
167the callback, the size of the internal buffer and the ID of the device you want to emit or capture audio from.
168
169Once 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
170beforehand. This gives the application complete control over how the memory is allocated. In the example below we initialize a playback device on the stack,
171but you could allocate it on the heap if that suits your situation better.
172
173 ```c
174 void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
175 {
176 // In playback mode copy data to pOutput. In capture mode read data from pInput. In full-duplex mode, both pOutput and pInput will be valid and you can
177 // move data from pInput into pOutput. Never process more than frameCount frames.
178 }
179
180 ...
181
182 ma_device_config config = ma_device_config_init(ma_device_type_playback);
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; // Can be accessed from the device object (device.pUserData).
188
189 ma_device device;
190 if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) {
191 ... An error occurred ...
192 }
193
194 ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually.
195
196 ...
197
198 ma_device_uninit(&device); // This will stop the device so no need to do that manually.
199 ```
200
201In 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
202from 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
203extract 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
204buffer. 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.
205The 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
206device 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
207a 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
208for the second frame, etc.
209
210The 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
211important 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
212are 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()`
213takes 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
214backends). The `config.playback.format` member sets the sample format which can be one of the following (all formats are native-endian):
215
216 |---------------|----------------------------------------|---------------------------|
217 | Symbol | Description | Range |
218 |---------------|----------------------------------------|---------------------------|
219 | ma_format_f32 | 32-bit floating point | [-1, 1] |
220 | ma_format_s16 | 16-bit signed integer | [-32768, 32767] |
221 | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] |
222 | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] |
223 | ma_format_u8 | 8-bit unsigned integer | [0, 255] |
224 |---------------|----------------------------------------|---------------------------|
225
226The `config.playback.channels` member sets the number of channels to use with the device. The channel count cannot exceed MA_MAX_CHANNELS. The
227`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
22844100 or 48000, but can be set to anything. It's recommended to keep this between 8000 and 384000, however.
229
230Note 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
231which is useful if you want to avoid the overhead of miniaudio's automatic data conversion.
232
233In 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
234not 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
235structures are transparent.
236
237Initializing 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
238`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
239it, 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.
240Note 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
241event indicating that the device needs to stop and handle it in a different thread. The following APIs must never be called inside the callback:
242
243 ma_device_init()
244 ma_device_init_ex()
245 ma_device_uninit()
246 ma_device_start()
247 ma_device_stop()
248
249You 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
250are 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 real-
251time processing thing which is beyond the scope of this introduction.
252
253The 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
254from `ma_device_type_playback` to `ma_device_type_capture` when setting up the config, like so:
255
256 ```c
257 ma_device_config config = ma_device_config_init(ma_device_type_capture);
258 config.capture.format = MY_FORMAT;
259 config.capture.channels = MY_CHANNELS;
260 ```
261
262In 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
263device type is set to `ma_device_type_capture`).
264
265These are the available device types and how you should handle the buffers in the callback:
266
267 |-------------------------|--------------------------------------------------------|
268 | Device Type | Callback Behavior |
269 |-------------------------|--------------------------------------------------------|
270 | ma_device_type_playback | Write to output buffer, leave input buffer untouched. |
271 | ma_device_type_capture | Read from input buffer, leave output buffer untouched. |
272 | ma_device_type_duplex | Read from input buffer, write to output buffer. |
273 | ma_device_type_loopback | Read from input buffer, leave output buffer untouched. |
274 |-------------------------|--------------------------------------------------------|
275
276You 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
277data 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
278channel), 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
279will need to convert the data yourself. There are functions available to help you do this which will be explained later.
280
281The 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
282devices connected and you want to use a specific one you will need to specify the device ID in the configuration, like so:
283
284 ```
285 config.playback.pDeviceID = pMyPlaybackDeviceID; // Only if requesting a playback or duplex device.
286 config.capture.pDeviceID = pMyCaptureDeviceID; // Only if requesting a capture, duplex or loopback device.
287 ```
288
289To retrieve the device ID you will need to perform device enumeration, however this requires the use of a new concept call the "context". Conceptually speaking
290the 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 and to
291perform operations outside the scope of an individual device. Mainly it is used for performing run-time linking against backend libraries, initializing
292backends and enumerating devices. The example below shows how to enumerate devices.
293
294 ```c
295 ma_context context;
296 if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) {
297 // Error.
298 }
299
300 ma_device_info* pPlaybackDeviceInfos;
301 ma_uint32 playbackDeviceCount;
302 ma_device_info* pCaptureDeviceInfos;
303 ma_uint32 captureDeviceCount;
304 if (ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, &pCaptureDeviceInfos, &captureDeviceCount) != MA_SUCCESS) {
305 // Error.
306 }
307
308 // Loop over the each device info and do something with it. Here we just print the name with their index. You may want to give the user the
309 // opportunity to choose which device they'd prefer.
310 for (ma_uint32 iDevice = 0; iDevice < playbackDeviceCount; iDevice += 1) {
311 printf("%d - %s\n", iDevice, pPlaybackDeviceInfos[iDevice].name);
312 }
313
314 ma_device_config config = ma_device_config_init(ma_device_type_playback);
315 config.playback.pDeviceID = &pPlaybackDeviceInfos[chosenPlaybackDeviceIndex].id;
316 config.playback.format = MY_FORMAT;
317 config.playback.channels = MY_CHANNEL_COUNT;
318 config.sampleRate = MY_SAMPLE_RATE;
319 config.dataCallback = data_callback;
320 config.pUserData = pMyCustomData;
321
322 ma_device device;
323 if (ma_device_init(&context, &config, &device) != MA_SUCCESS) {
324 // Error
325 }
326
327 ...
328
329 ma_device_uninit(&device);
330 ma_context_uninit(&context);
331 ```
332
333The 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`
334values 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
335parameter is the number of backends listed in the array pointed to by the first paramter. The third parameter is a pointer to a `ma_context_config` object
336which can be NULL, in which case defaults are used. The context configuration is used for setting the logging callback, custom memory allocation callbacks,
337user-defined data and some backend-specific configurations.
338
339Once 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
340callback for handling devices by using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer to a pointer that will,
341upon 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
342receive the number of items in the returned buffer. Do not free the returned buffers as their memory is managed internally by miniaudio.
343
344The `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
345for presenting a list of devices to the user via the UI.
346
347When 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,
348will 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
349only 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
350allocate memory for the context.
351
352
353
354Building
355========
356miniaudio should work cleanly out of the box without the need to download or install any dependencies. See below for platform-specific details.
357
358
359Windows
360-------
361The Windows build should compile clean on all popular compilers without the need to configure any include paths nor link to any libraries.
362
363macOS and iOS
364-------------
365The macOS build should compile clean without the need to download any dependencies or link to any libraries or frameworks. The iOS build needs to be compiled
366as Objective-C (sorry) and will need to link the relevant frameworks but should Just Work with Xcode. Compiling through the command line requires linking to
367-lpthread and -lm.
368
369Linux
370-----
371The Linux build only requires linking to -ldl, -lpthread and -lm. You do not need any development packages.
372
373BSD
374---
375The BSD build only requires linking to -lpthread and -lm. NetBSD uses audio(4), OpenBSD uses sndio and FreeBSD uses OSS.
376
377Android
378-------
379AAudio is the highest priority backend on Android. This should work out out of the box without needing any kind of compiler configuration. Support for AAudio
380starts with Android 8 which means older versions will fall back to OpenSL|ES which requires API level 16+.
381
382Emscripten
383----------
384The Emscripten build emits Web Audio JavaScript directly and should Just Work without any configuration. You cannot use -std=c* compiler flags, nor -ansi.
385
386
387Build Options
388-------------
389#define these options before including miniaudio.h.
390
391#define MA_NO_WASAPI
392 Disables the WASAPI backend.
393
394#define MA_NO_DSOUND
395 Disables the DirectSound backend.
396
397#define MA_NO_WINMM
398 Disables the WinMM backend.
399
400#define MA_NO_ALSA
401 Disables the ALSA backend.
402
403#define MA_NO_PULSEAUDIO
404 Disables the PulseAudio backend.
405
406#define MA_NO_JACK
407 Disables the JACK backend.
408
409#define MA_NO_COREAUDIO
410 Disables the Core Audio backend.
411
412#define MA_NO_SNDIO
413 Disables the sndio backend.
414
415#define MA_NO_AUDIO4
416 Disables the audio(4) backend.
417
418#define MA_NO_OSS
419 Disables the OSS backend.
420
421#define MA_NO_AAUDIO
422 Disables the AAudio backend.
423
424#define MA_NO_OPENSL
425 Disables the OpenSL|ES backend.
426
427#define MA_NO_WEBAUDIO
428 Disables the Web Audio backend.
429
430#define MA_NO_NULL
431 Disables the null backend.
432
433#define MA_NO_DECODING
434 Disables the decoding APIs.
435
436#define MA_NO_DEVICE_IO
437 Disables playback and recording. This will disable ma_context and ma_device APIs. This is useful if you only want to use miniaudio's data conversion and/or
438 decoding APIs.
439
440#define MA_NO_STDIO
441 Disables file IO APIs.
442
443#define MA_NO_SSE2
444 Disables SSE2 optimizations.
445
446#define MA_NO_AVX2
447 Disables AVX2 optimizations.
448
449#define MA_NO_AVX512
450 Disables AVX-512 optimizations.
451
452#define MA_NO_NEON
453 Disables NEON optimizations.
454
455#define MA_LOG_LEVEL <Level>
456 Sets the logging level. Set level to one of the following:
457 MA_LOG_LEVEL_VERBOSE
458 MA_LOG_LEVEL_INFO
459 MA_LOG_LEVEL_WARNING
460 MA_LOG_LEVEL_ERROR
461
462#define MA_DEBUG_OUTPUT
463 Enable printf() debug output.
464
465#define MA_COINIT_VALUE
466 Windows only. The value to pass to internal calls to CoInitializeEx(). Defaults to COINIT_MULTITHREADED.
467
468
469
470Definitions
471===========
472This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity in the use of terms throughout the audio space, so this
473section is intended to clarify how miniaudio uses each term.
474
475Sample
476------
477A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit floating point number.
478
479Frame / PCM Frame
480-----------------
481A frame is a groups 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
482is 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
483needs 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" or whatnot.
484
485Channel
486-------
487A 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
488stereo 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
489a 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
490should not be confused.
491
492Sample Rate
493-----------
494The 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.
495
496Formats
497-------
498Throughout miniaudio you will see references to different sample formats:
499
500 |---------------|----------------------------------------|---------------------------|
501 | Symbol | Description | Range |
502 |---------------|----------------------------------------|---------------------------|
503 | ma_format_f32 | 32-bit floating point | [-1, 1] |
504 | ma_format_s16 | 16-bit signed integer | [-32768, 32767] |
505 | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] |
506 | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] |
507 | ma_format_u8 | 8-bit unsigned integer | [0, 255] |
508 |---------------|----------------------------------------|---------------------------|
509
510All formats are native-endian.
511
512
513
514Decoding
515========
516The `ma_decoder` API is used for reading audio files. To enable a decoder you must #include the header of the relevant backend library before the
517implementation of miniaudio. You can find copies of these in the "extras" folder in the miniaudio repository (https://github.com/dr-soft/miniaudio).
518
519The table below are the supported decoding backends:
520
521 |--------|-----------------|
522 | Type | Backend Library |
523 |--------|-----------------|
524 | WAV | dr_wav.h |
525 | FLAC | dr_flac.h |
526 | MP3 | dr_mp3.h |
527 | Vorbis | stb_vorbis.c |
528 |--------|-----------------|
529
530The code below is an example of how to enable decoding backends:
531
532 ```c
533 #include "dr_flac.h" // Enables FLAC decoding.
534 #include "dr_mp3.h" // Enables MP3 decoding.
535 #include "dr_wav.h" // Enables WAV decoding.
536
537 #define MINIAUDIO_IMPLEMENTATION
538 #include "miniaudio.h"
539 ```
540
541A 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
542with `ma_decoder_init()`. Here is an example for loading a decoder from a file:
543
544 ```c
545 ma_decoder decoder;
546 ma_result result = ma_decoder_init_file("MySong.mp3", NULL, &decoder);
547 if (result != MA_SUCCESS) {
548 return false; // An error occurred.
549 }
550
551 ...
552
553 ma_decoder_uninit(&decoder);
554 ```
555
556When 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 to
557configure the output format, channel count, sample rate and channel map:
558
559 ```c
560 ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000);
561 ```
562
563When passing in NULL for decoder config in `ma_decoder_init*()`, the output format will be the same as that defined by the decoding backend.
564
565Data is read from the decoder as PCM frames:
566
567 ```c
568 ma_uint64 framesRead = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead);
569 ```
570
571You can also seek to a specific frame like so:
572
573 ```c
574 ma_result result = ma_decoder_seek_to_pcm_frame(pDecoder, targetFrame);
575 if (result != MA_SUCCESS) {
576 return false; // An error occurred.
577 }
578 ```
579
580When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding backend. This can be unnecessarily inefficient if the type
581is already known. In this case you can use the `_wav`, `_mp3`, etc. varients of the aforementioned initialization APIs:
582
583 ```c
584 ma_decoder_init_wav()
585 ma_decoder_init_mp3()
586 ma_decoder_init_memory_wav()
587 ma_decoder_init_memory_mp3()
588 ma_decoder_init_file_wav()
589 ma_decoder_init_file_mp3()
590 etc.
591 ```
592
593The `ma_decoder_init_file()` API will try using the file extension to determine which decoding backend to prefer.
594
595
596
597Sample Format Conversion
598========================
599Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and `ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()`
600to 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
601PCM frames where you want to specify the frame count and channel count as a variable instead of the total sample count.
602
603Dithering
604---------
605Dithering can be set using ditherMode parmater.
606
607The different dithering modes include the following, in order of efficiency:
608
609 |-----------|--------------------------|
610 | Type | Enum Token |
611 |-----------|--------------------------|
612 | None | ma_dither_mode_none |
613 | Rectangle | ma_dither_mode_rectangle |
614 | Triangle | ma_dither_mode_triangle |
615 |-----------|--------------------------|
616
617Note 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.
618Dithering is available for the following conversions:
619
620 s16 -> u8
621 s24 -> u8
622 s32 -> u8
623 f32 -> u8
624 s24 -> s16
625 s32 -> s16
626 f32 -> s16
627
628Note 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.
629
630
631
632Channel Conversion
633==================
634Channel conversion is used for channel rearrangement and conversion from one channel count to another. The `ma_channel_converter` API is used for channel
635conversion. Below is an example of initializing a simple channel converter which converts from mono to stereo.
636
637 ```c
638 ma_channel_converter_config config = ma_channel_converter_config_init(ma_format, 1, NULL, 2, NULL, ma_channel_mix_mode_default, NULL);
639 result = ma_channel_converter_init(&config, &converter);
640 if (result != MA_SUCCESS) {
641 // Error.
642 }
643 ```
644
645To process perform the conversion simply call `ma_channel_converter_process_pcm_frames()` like so:
646
647 ```c
648 ma_result result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount);
649 if (result != MA_SUCCESS) {
650 // Error.
651 }
652 ```
653
654It is up to the caller to ensure the output buffer is large enough to accomodate the new PCM frames.
655
656The only formats supported are `ma_format_s16` and `ma_format_f32`. If you need another format you need to convert your data manually which you can do with
657`ma_pcm_convert()`, etc.
658
659Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported.
660
661
662Channel Mapping
663---------------
664In addition to converting from one channel count to another, like the example above, The channel converter can also be used to rearrange channels. When
665initializing 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
666channel map contains the same channel positions with the exception that they're in a different order, a simple shuffling of the channels with be performed. If,
667however, 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
668mode which is specified when initializing the `ma_channel_converter_config` object.
669
670When 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
671channel is simply averaged and copied to the mono channel.
672
673In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess channels and silence extra channels. For example, converting
674from 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.
675
676The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a simple distribution between input and output. Imagine sitting
677in 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
678of the front and left walls.
679
680Finally, 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
681`ma_channel_converter_config_init()`.
682
683Predefined 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
684be one of the following:
685
686 |-----------------------------------|-----------------------------------------------------------|
687 | Name | Description |
688 |-----------------------------------|-----------------------------------------------------------|
689 | ma_standard_channel_map_default | Default channel map used by miniaudio. See below. |
690 | ma_standard_channel_map_microsoft | Channel map used by Microsoft's bitfield channel maps. |
691 | ma_standard_channel_map_alsa | Default ALSA channel map. |
692 | ma_standard_channel_map_rfc3551 | RFC 3551. Based on AIFF. |
693 | ma_standard_channel_map_flac | FLAC channel map. |
694 | ma_standard_channel_map_vorbis | Vorbis channel map. |
695 | ma_standard_channel_map_sound4 | FreeBSD's sound(4). |
696 | ma_standard_channel_map_sndio | sndio channel map. www.sndio.org/tips.html |
697 | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering |
698 |-----------------------------------|-----------------------------------------------------------|
699
700Below are the channel maps used by default in miniaudio (ma_standard_channel_map_default):
701
702 |---------------|------------------------------|
703 | Channel Count | Mapping |
704 |---------------|------------------------------|
705 | 1 (Mono) | 0: MA_CHANNEL_MONO |
706 |---------------|------------------------------|
707 | 2 (Stereo) | 0: MA_CHANNEL_FRONT_LEFT |
708 | | 1: MA_CHANNEL_FRONT_RIGHT |
709 |---------------|------------------------------|
710 | 3 | 0: MA_CHANNEL_FRONT_LEFT |
711 | | 1: MA_CHANNEL_FRONT_RIGHT |
712 | | 2: MA_CHANNEL_FRONT_CENTER |
713 |---------------|------------------------------|
714 | 4 (Surround) | 0: MA_CHANNEL_FRONT_LEFT |
715 | | 1: MA_CHANNEL_FRONT_RIGHT |
716 | | 2: MA_CHANNEL_FRONT_CENTER |
717 | | 3: MA_CHANNEL_BACK_CENTER |
718 |---------------|------------------------------|
719 | 5 | 0: MA_CHANNEL_FRONT_LEFT |
720 | | 1: MA_CHANNEL_FRONT_RIGHT |
721 | | 2: MA_CHANNEL_FRONT_CENTER |
722 | | 3: MA_CHANNEL_BACK_LEFT |
723 | | 4: MA_CHANNEL_BACK_RIGHT |
724 |---------------|------------------------------|
725 | 6 (5.1) | 0: MA_CHANNEL_FRONT_LEFT |
726 | | 1: MA_CHANNEL_FRONT_RIGHT |
727 | | 2: MA_CHANNEL_FRONT_CENTER |
728 | | 3: MA_CHANNEL_LFE |
729 | | 4: MA_CHANNEL_SIDE_LEFT |
730 | | 5: MA_CHANNEL_SIDE_RIGHT |
731 |---------------|------------------------------|
732 | 7 | 0: MA_CHANNEL_FRONT_LEFT |
733 | | 1: MA_CHANNEL_FRONT_RIGHT |
734 | | 2: MA_CHANNEL_FRONT_CENTER |
735 | | 3: MA_CHANNEL_LFE |
736 | | 4: MA_CHANNEL_BACK_CENTER |
737 | | 4: MA_CHANNEL_SIDE_LEFT |
738 | | 5: MA_CHANNEL_SIDE_RIGHT |
739 |---------------|------------------------------|
740 | 8 (7.1) | 0: MA_CHANNEL_FRONT_LEFT |
741 | | 1: MA_CHANNEL_FRONT_RIGHT |
742 | | 2: MA_CHANNEL_FRONT_CENTER |
743 | | 3: MA_CHANNEL_LFE |
744 | | 4: MA_CHANNEL_BACK_LEFT |
745 | | 5: MA_CHANNEL_BACK_RIGHT |
746 | | 6: MA_CHANNEL_SIDE_LEFT |
747 | | 7: MA_CHANNEL_SIDE_RIGHT |
748 |---------------|------------------------------|
749 | Other | All channels set to 0. This |
750 | | is equivalent to the same |
751 | | mapping as the device. |
752 |---------------|------------------------------|
753
754
755
756Resampling
757==========
758Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something like the following:
759
760 ```c
761 ma_resampler_config config = ma_resampler_config_init(ma_format_s16, channels, sampleRateIn, sampleRateOut, ma_resample_algorithm_linear);
762 ma_resampler resampler;
763 ma_result result = ma_resampler_init(&config, &resampler);
764 if (result != MA_SUCCESS) {
765 // An error occurred...
766 }
767 ```
768
769Do the following to uninitialize the resampler:
770
771 ```c
772 ma_resampler_uninit(&resampler);
773 ```
774
775The following example shows how data can be processed
776
777 ```c
778 ma_uint64 frameCountIn = 1000;
779 ma_uint64 frameCountOut = 2000;
780 ma_result result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);
781 if (result != MA_SUCCESS) {
782 // An error occurred...
783 }
784
785 // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number of output frames written.
786 ```
787
788To 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
789you want to use, the number of channels, the input and output sample rate, and the algorithm.
790
791The 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
792where necessary. Note that the format is the same for both input and output. The format cannot be changed after initialization.
793
794The resampler supports multiple channels and is always interleaved (both input and output). The channel count cannot be changed after initialization.
795
796The 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
797only configuration property that can be changed after initialization.
798
799The miniaudio resampler supports multiple algorithms:
800
801 |-----------|------------------------------|
802 | Algorithm | Enum Token |
803 |-----------|------------------------------|
804 | Linear | ma_resample_algorithm_linear |
805 | Speex | ma_resample_algorithm_speex |
806 |-----------|------------------------------|
807
808Because 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
809it'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
810the Speex Resampler section below.
811
812The algorithm cannot be changed after initialization.
813
814Processing always happens on a per PCM frame basis and always assumes interleaved input and output. De-interleaved processing is not supported. To process
815frames, 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
816input 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
817number 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
818buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated as seek.
819
820The 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
821ratio with `ma_resampler_set_rate_ratio()`. The ratio is in/out.
822
823Sometimes 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
824`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
825input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`.
826
827Due 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
828with `ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`.
829
830
831Resampling Algorithms
832---------------------
833The choice of resampling algorithm depends on your situation and requirements. The linear resampler is the most efficient and has the least amount of latency,
834but at the expense of poorer quality. The Speex resampler is higher quality, but slower with more latency. It also performs several heap applications
835internally for memory management.
836
837
838Linear Resampling
839-----------------
840The 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
841may make it a suitable option depending on your requirements.
842
843The linear resampler performs low-pass filtering before or after downsampling or upsampling, depending on the sample rates you're converting between. When
844decreasing the sample rate, the low-pass filter will be applied before downsampling. When increasing the rate it will be performed after upsampling. By default
845a second order low-pass filter will be applied. To improve quality you can chain low-pass filters together, up to a maximum of `MA_MAX_RESAMPLER_LPF_FILTERS`.
846This comes at the expense of increased computational cost and latency. You can also disable filtering altogether by setting the filter count to 0. The filter
847count is controlled with the `lpfCount` config variable.
848
849The 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
850can 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
851sense 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.
852Values 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
853and is a purely perceptual configuration.
854
855The API for the linear resampler is the same as the main resampler API, only it's called `ma_linear_resampler`.
856
857
858Speex Resampling
859----------------
860The 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
861domain, 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
862source files. To opt-in, you must first #include the following file before the implementation of miniaudio.h:
863
864 #include "extras/speex_resampler/ma_speex_resampler.h"
865
866Both the header and implementation is contained within the same file. To implementation can be included in your program like so:
867
868 #define MINIAUDIO_SPEEX_RESAMPLER_IMPLEMENTATION
869 #include "extras/speex_resampler/ma_speex_resampler.h"
870
871Note 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
872initializing. If you try to use the Speex resampler without opting in, initialization of the `ma_resampler` object will fail with `MA_NO_BACKEND`.
873
874The 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
875the worst/fastest and 10 being the best/slowest. The default value is 3.
876
877
878
879
880General Data Conversion
881=======================
882The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and resampling into one operation. This is what miniaudio uses
883internally 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
884conversion is very similar to the resampling API. Create a `ma_data_converter` object like this:
885
886 ```c
887 ma_data_converter_config config = ma_data_converter_config_init(inputFormat, outputFormat, inputChannels, outputChannels, inputSampleRate, outputSampleRate);
888 ma_data_converter converter;
889 ma_result result = ma_data_converter_init(&config, &converter);
890 if (result != MA_SUCCESS) {
891 // An error occurred...
892 }
893 ```
894
895In 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
896channel maps and resampling quality. Something like the following may be more suitable depending on your requirements:
897
898 ```c
899 ma_data_converter_config config = ma_data_converter_config_init_default();
900 config.formatIn = inputFormat;
901 config.formatOut = outputFormat;
902 config.channelsIn = inputChannels;
903 config.channelsOut = outputChannels;
904 config.sampleRateIn = inputSampleRate;
905 config.sampleRateOut = outputSampleRate;
906 ma_get_standard_channel_map(ma_standard_channel_map_flac, config.channelCountIn, config.channelMapIn);
907 config.resampling.linear.lpfCount = MA_MAX_RESAMPLER_LPF_FILTERS;
908 ```
909
910Do the following to uninitialize the data converter:
911
912 ```c
913 ma_data_converter_uninit(&converter);
914 ```
915
916The following example shows how data can be processed
917
918 ```c
919 ma_uint64 frameCountIn = 1000;
920 ma_uint64 frameCountOut = 2000;
921 ma_result result = ma_data_converter_process_pcm_frames(&converter, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);
922 if (result != MA_SUCCESS) {
923 // An error occurred...
924 }
925
926 // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number of output frames written.
927 ```
928
929The data converter supports multiple channels and is always interleaved (both input and output). The channel count cannot be changed after initialization.
930
931The 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
932only configuration property that can be changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of `ma_data_converter_config`
933is 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
934resampling algorithm cannot be changed after initialization.
935
936Processing always happens on a per PCM frame basis and always assumes interleaved input and output. De-interleaved processing is not supported. To process
937frames, 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
938of 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
939number 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
940buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated as seek.
941
942Sometimes 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
943`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
944input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`.
945
946Due 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
947input rate and the output rate with `ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`.
948
949
950
951
952Biquad Filtering
953================
954Biquad filtering is achieved with the `ma_biquad` API. Example:
955
956 ```c
957 ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);
958 ma_result result = ma_biquad_init(&config, &biquad);
959 if (result != MA_SUCCESS) {
960 // Error.
961 }
962
963 ...
964
965 ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount);
966 ```
967
968Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0, b1 and b2, and the denominator coefficients are a0, a1 and
969a2. The a0 coefficient is required and coefficients must not be pre-normalized.
970
971Supported 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
972`ma_format_s16` the biquad filter will use fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used.
973
974Input and output frames are always interleaved.
975
976Filtering can be applied in-place by passing in the same pointer for both the input and output buffers, like so:
977
978 ```c
979 ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount);
980 ```
981
982If 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
983need to change the properties of the filter while keeping the values of registers valid to avoid glitching or whatnot. Do not use `ma_biquad_init()` for this
984as it will do a full initialization which involves clearing the registers to 0. Note that changing the format or channel count after initialization is invalid
985and will result in an error.
986
987
988
989Low-Pass, High-Pass and Band-Pass Filtering
990===========================================
991Low-pass, high-pass and band-pass filtering is achieved with the `ma_lpf`, `ma_hpf` and `ma_bpf` APIs respective. Low-pass filter example:
992
993 ```c
994 ma_lpf_config config = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency);
995 ma_result result = ma_lpf_init(&config, &lpf);
996 if (result != MA_SUCCESS) {
997 // Error.
998 }
999
1000 ...
1001
1002 ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount);
1003 ```
1004
1005Supported 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
1006frames are always interleaved.
1007
1008Filtering can be applied in-place by passing in the same pointer for both the input and output buffers, like so:
1009
1010 ```c
1011 ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount);
1012 ```
1013
1014These filters are implemented as a biquad filter. If you need to increase the filter order, simply chain multiple filters together.
1015
1016 ```c
1017 for (iFilter = 0; iFilter < filterCount; iFilter += 1) {
1018 ma_lpf_process_pcm_frames(&lpf[iFilter], pMyData, pMyData, frameCount);
1019 }
1020 ```
1021
1022If 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
1023useful if you need to change the sample rate and/or cutoff frequency dynamically while maintaing smooth transitions. Note that changing the format or channel
1024count after initialization is invalid and will result in an error.
1025
1026The example code above is for low-pass filters, but the same applies for high-pass and band-pass filters, only you should use the `ma_hpf` and `ma_bpf` APIs
1027instead.
1028
1029
1030
1031Waveforms
1032=========
1033miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved with the `ma_waveform` API. Example:
1034
1035 ```c
1036 ma_waveform waveform;
1037 ma_result result = ma_waveform_init(ma_waveform_type_sine, amplitude, frequency, sampleRate, &waveform);
1038 if (result != MA_SUCCESS) {
1039 // Error.
1040 }
1041
1042 ...
1043
1044 ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount, FORMAT, CHANNELS);
1045 ```
1046
1047The amplitude, frequency and sample rate can be changed dynamically with `ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()` and
1048`ma_waveform_set_sample_rate()` respectively.
1049
1050
1051
1052Ring Buffers
1053============
1054miniaudio 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
1055on 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`.
1056
1057Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved streams. The caller can also allocate their own backing memory for
1058the ring buffer to use internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for you.
1059
1060The 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
1061something like the following:
1062
1063 ```c
1064 ma_pcm_rb rb;
1065 ma_result result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb);
1066 if (result != MA_SUCCESS) {
1067 // Error
1068 }
1069 ```
1070
1071The `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
1072ring 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
1073fourth parameter is an optional pre-allocated buffer and the fifth parameter is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation
1074routines. Passing in NULL for this results in MA_MALLOC() and MA_FREE() being used.
1075
1076Use `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 sub-
1077buffers you can use `ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and `ma_pcm_rb_get_subbuffer_ptr()`.
1078
1079Use '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
1080need, 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
1081a 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.
1082
1083After 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
1084`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
1085call 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
1086`ma_pcm_rb_commit_write()` is what's used to increment the pointers.
1087
1088If 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()`,
1089`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
1090the 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
1091there is too little space between the pointers, move the write pointer forward.
1092
1093You 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 sample, only you will use the `ma_rb`
1094functions instead of `ma_pcm_rb` and instead of frame counts you'll pass around byte counts.
1095
1096The maximum size of the buffer in bytes is 0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1) due to the most significant bit being used to encode a flag and the internally
1097managed buffers always being aligned to MA_SIMD_ALIGNMENT.
1098
1099Note that the ring buffer is only thread safe when used by a single consumer thread and single producer thread.
1100
1101
1102
1103Backends
1104========
1105The following backends are supported by miniaudio.
1106
1107 |-------------|-----------------------|--------------------------------------------------------|
1108 | Name | Enum Name | Supported Operating Systems |
1109 |-------------|-----------------------|--------------------------------------------------------|
1110 | WASAPI | ma_backend_wasapi | Windows Vista+ |
1111 | DirectSound | ma_backend_dsound | Windows XP+ |
1112 | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) |
1113 | Core Audio | ma_backend_coreaudio | macOS, iOS |
1114 | ALSA | ma_backend_alsa | Linux |
1115 | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
1116 | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
1117 | sndio | ma_backend_sndio | OpenBSD |
1118 | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
1119 | OSS | ma_backend_oss | FreeBSD |
1120 | AAudio | ma_backend_aaudio | Android 8+ |
1121 | OpenSL|ES | ma_backend_opensl | Android (API level 16+) |
1122 | Web Audio | ma_backend_webaudio | Web (via Emscripten) |
1123 | Null | ma_backend_null | Cross Platform (not used on Web) |
1124 |-------------|-----------------------|--------------------------------------------------------|
1125
1126Some backends have some nuance details you may want to be aware of.
1127
1128WASAPI
1129------
1130- 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
1131 this, set wasapi.noAutoConvertSRC to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing when the
1132 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM flag is specified. Setting wasapi.noAutoConvertSRC will result in miniaudio's lower quality internal resampler being used
1133 instead which will in turn enable the use of low-latency shared mode.
1134
1135PulseAudio
1136----------
1137- If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki:
1138 https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling
1139 Alternatively, consider using a different backend such as ALSA.
1140
1141Android
1142-------
1143- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest:
1144 <uses-permission android:name="android.permission.RECORD_AUDIO" />
1145- With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a limitation with OpenSL|ES.
1146- 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
1147 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().
1148- 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
1149 potential device-specific optimizations the driver may implement.
1150
1151UWP
1152---
1153- UWP only supports default playback and capture devices.
1154- UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest):
1155 <Package ...>
1156 ...
1157 <Capabilities>
1158 <DeviceCapability Name="microphone" />
1159 </Capabilities>
1160 </Package>
1161
1162Web Audio / Emscripten
1163----------------------
1164- You cannot use -std=c* compiler flags, nor -ansi. This only applies to the Emscripten build.
1165- 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.
1166- Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as they've been deprecated.
1167- 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
1168 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
1169 without first handling some kind of user input.
1170
1171
1172
1173Miscellaneous Notes
1174===================
1175- Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for WASAPI and Core Audio, however other backends such as
1176 PulseAudio may naturally support it, though not all have been tested.
1177- The contents of the output buffer passed into the data callback will always be pre-initialized to zero unless the noPreZeroedOutputBuffer config variable in
1178 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.
1179- 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
1180 clipping yourself, you can disable this overhead by setting noClip to true in the device config.
1181- The sndio backend is currently only enabled on OpenBSD builds.
1182- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can use it.
1183- Note that GCC and Clang requires "-msse2", "-mavx2", etc. for SIMD optimizations.
1184*/
1185
1186#ifndef miniaudio_h
1187#define miniaudio_h
1188
1189#ifdef __cplusplus
1190extern "C" {
1191#endif
1192
1193#if defined(_MSC_VER) && !defined(__clang__)
1194 #pragma warning(push)
1195 #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
1196 #pragma warning(disable:4324) /* structure was padded due to alignment specifier */
1197#else
1198 #pragma GCC diagnostic push
1199 #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
1200 #if defined(__clang__)
1201 #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */
1202 #endif
1203#endif
1204
1205/* Platform/backend detection. */
1206#ifdef _WIN32
1207 #define MA_WIN32
1208 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP || WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
1209 #define MA_WIN32_UWP
1210 #else
1211 #define MA_WIN32_DESKTOP
1212 #endif
1213#else
1214 #define MA_POSIX
1215 #include <pthread.h> /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */
1216 #include <semaphore.h>
1217
1218 #ifdef __unix__
1219 #define MA_UNIX
1220 #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
1221 #define MA_BSD
1222 #endif
1223 #endif
1224 #ifdef __linux__
1225 #define MA_LINUX
1226 #endif
1227 #ifdef __APPLE__
1228 #define MA_APPLE
1229 #endif
1230 #ifdef __ANDROID__
1231 #define MA_ANDROID
1232 #endif
1233 #ifdef __EMSCRIPTEN__
1234 #define MA_EMSCRIPTEN
1235 #endif
1236#endif
1237
1238#include <stddef.h> /* For size_t. */
1239
1240/* Sized types. Prefer built-in types. Fall back to stdint. */
1241#ifdef _MSC_VER
1242 #if defined(__clang__)
1243 #pragma GCC diagnostic push
1244 #pragma GCC diagnostic ignored "-Wlanguage-extension-token"
1245 #pragma GCC diagnostic ignored "-Wlong-long"
1246 #pragma GCC diagnostic ignored "-Wc++11-long-long"
1247 #endif
1248 typedef signed __int8 ma_int8;
1249 typedef unsigned __int8 ma_uint8;
1250 typedef signed __int16 ma_int16;
1251 typedef unsigned __int16 ma_uint16;
1252 typedef signed __int32 ma_int32;
1253 typedef unsigned __int32 ma_uint32;
1254 typedef signed __int64 ma_int64;
1255 typedef unsigned __int64 ma_uint64;
1256 #if defined(__clang__)
1257 #pragma GCC diagnostic pop
1258 #endif
1259#else
1260 #define MA_HAS_STDINT
1261 #include <stdint.h>
1262 typedef int8_t ma_int8;
1263 typedef uint8_t ma_uint8;
1264 typedef int16_t ma_int16;
1265 typedef uint16_t ma_uint16;
1266 typedef int32_t ma_int32;
1267 typedef uint32_t ma_uint32;
1268 typedef int64_t ma_int64;
1269 typedef uint64_t ma_uint64;
1270#endif
1271
1272#ifdef MA_HAS_STDINT
1273 typedef uintptr_t ma_uintptr;
1274#else
1275 #if defined(_WIN32)
1276 #if defined(_WIN64)
1277 typedef ma_uint64 ma_uintptr;
1278 #else
1279 typedef ma_uint32 ma_uintptr;
1280 #endif
1281 #elif defined(__GNUC__)
1282 #if defined(__LP64__)
1283 typedef ma_uint64 ma_uintptr;
1284 #else
1285 typedef ma_uint32 ma_uintptr;
1286 #endif
1287 #else
1288 typedef ma_uint64 ma_uintptr; /* Fallback. */
1289 #endif
1290#endif
1291
1292typedef ma_uint8 ma_bool8;
1293typedef ma_uint32 ma_bool32;
1294#define MA_TRUE 1
1295#define MA_FALSE 0
1296
1297typedef void* ma_handle;
1298typedef void* ma_ptr;
1299typedef void (* ma_proc)(void);
1300
1301#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED)
1302typedef ma_uint16 wchar_t;
1303#endif
1304
1305/* Define NULL for some compilers. */
1306#ifndef NULL
1307#define NULL 0
1308#endif
1309
1310#if defined(SIZE_MAX)
1311 #define MA_SIZE_MAX SIZE_MAX
1312#else
1313 #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */
1314#endif
1315
1316
1317#ifdef _MSC_VER
1318 #define MA_INLINE __forceinline
1319#elif defined(__GNUC__)
1320 /*
1321 I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
1322 the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
1323 case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
1324 command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
1325 I am using "__inline__" only when we're compiling in strict ANSI mode.
1326 */
1327 #if defined(__STRICT_ANSI__)
1328 #define MA_INLINE __inline__ __attribute__((always_inline))
1329 #else
1330 #define MA_INLINE inline __attribute__((always_inline))
1331 #endif
1332#else
1333 #define MA_INLINE
1334#endif
1335
1336#if defined(_MSC_VER)
1337 #if _MSC_VER >= 1400
1338 #define MA_ALIGN(alignment) __declspec(align(alignment))
1339 #endif
1340#elif !defined(__DMC__)
1341 #define MA_ALIGN(alignment) __attribute__((aligned(alignment)))
1342#endif
1343#ifndef MA_ALIGN
1344 #define MA_ALIGN(alignment)
1345#endif
1346
1347/* SIMD alignment in bytes. Currently set to 64 bytes in preparation for future AVX-512 optimizations. */
1348#define MA_SIMD_ALIGNMENT 64
1349
1350
1351/* Logging levels */
1352#define MA_LOG_LEVEL_VERBOSE 4
1353#define MA_LOG_LEVEL_INFO 3
1354#define MA_LOG_LEVEL_WARNING 2
1355#define MA_LOG_LEVEL_ERROR 1
1356
1357#ifndef MA_LOG_LEVEL
1358#define MA_LOG_LEVEL MA_LOG_LEVEL_ERROR
1359#endif
1360
1361typedef struct ma_context ma_context;
1362typedef struct ma_device ma_device;
1363
1364typedef ma_uint8 ma_channel;
1365#define MA_CHANNEL_NONE 0
1366#define MA_CHANNEL_MONO 1
1367#define MA_CHANNEL_FRONT_LEFT 2
1368#define MA_CHANNEL_FRONT_RIGHT 3
1369#define MA_CHANNEL_FRONT_CENTER 4
1370#define MA_CHANNEL_LFE 5
1371#define MA_CHANNEL_BACK_LEFT 6
1372#define MA_CHANNEL_BACK_RIGHT 7
1373#define MA_CHANNEL_FRONT_LEFT_CENTER 8
1374#define MA_CHANNEL_FRONT_RIGHT_CENTER 9
1375#define MA_CHANNEL_BACK_CENTER 10
1376#define MA_CHANNEL_SIDE_LEFT 11
1377#define MA_CHANNEL_SIDE_RIGHT 12
1378#define MA_CHANNEL_TOP_CENTER 13
1379#define MA_CHANNEL_TOP_FRONT_LEFT 14
1380#define MA_CHANNEL_TOP_FRONT_CENTER 15
1381#define MA_CHANNEL_TOP_FRONT_RIGHT 16
1382#define MA_CHANNEL_TOP_BACK_LEFT 17
1383#define MA_CHANNEL_TOP_BACK_CENTER 18
1384#define MA_CHANNEL_TOP_BACK_RIGHT 19
1385#define MA_CHANNEL_AUX_0 20
1386#define MA_CHANNEL_AUX_1 21
1387#define MA_CHANNEL_AUX_2 22
1388#define MA_CHANNEL_AUX_3 23
1389#define MA_CHANNEL_AUX_4 24
1390#define MA_CHANNEL_AUX_5 25
1391#define MA_CHANNEL_AUX_6 26
1392#define MA_CHANNEL_AUX_7 27
1393#define MA_CHANNEL_AUX_8 28
1394#define MA_CHANNEL_AUX_9 29
1395#define MA_CHANNEL_AUX_10 30
1396#define MA_CHANNEL_AUX_11 31
1397#define MA_CHANNEL_AUX_12 32
1398#define MA_CHANNEL_AUX_13 33
1399#define MA_CHANNEL_AUX_14 34
1400#define MA_CHANNEL_AUX_15 35
1401#define MA_CHANNEL_AUX_16 36
1402#define MA_CHANNEL_AUX_17 37
1403#define MA_CHANNEL_AUX_18 38
1404#define MA_CHANNEL_AUX_19 39
1405#define MA_CHANNEL_AUX_20 40
1406#define MA_CHANNEL_AUX_21 41
1407#define MA_CHANNEL_AUX_22 42
1408#define MA_CHANNEL_AUX_23 43
1409#define MA_CHANNEL_AUX_24 44
1410#define MA_CHANNEL_AUX_25 45
1411#define MA_CHANNEL_AUX_26 46
1412#define MA_CHANNEL_AUX_27 47
1413#define MA_CHANNEL_AUX_28 48
1414#define MA_CHANNEL_AUX_29 49
1415#define MA_CHANNEL_AUX_30 50
1416#define MA_CHANNEL_AUX_31 51
1417#define MA_CHANNEL_LEFT MA_CHANNEL_FRONT_LEFT
1418#define MA_CHANNEL_RIGHT MA_CHANNEL_FRONT_RIGHT
1419#define MA_CHANNEL_POSITION_COUNT (MA_CHANNEL_AUX_31 + 1)
1420
1421
1422typedef int ma_result;
1423#define MA_SUCCESS 0
1424
1425/* General errors. */
1426#define MA_ERROR -1 /* A generic error. */
1427#define MA_INVALID_ARGS -2
1428#define MA_INVALID_OPERATION -3
1429#define MA_OUT_OF_MEMORY -4
1430#define MA_ACCESS_DENIED -5
1431#define MA_TOO_LARGE -6
1432#define MA_TIMEOUT -7
1433
1434/* General miniaudio-specific errors. */
1435#define MA_FORMAT_NOT_SUPPORTED -100
1436#define MA_DEVICE_TYPE_NOT_SUPPORTED -101
1437#define MA_SHARE_MODE_NOT_SUPPORTED -102
1438#define MA_NO_BACKEND -103
1439#define MA_NO_DEVICE -104
1440#define MA_API_NOT_FOUND -105
1441#define MA_INVALID_DEVICE_CONFIG -106
1442
1443/* State errors. */
1444#define MA_DEVICE_BUSY -200
1445#define MA_DEVICE_NOT_INITIALIZED -201
1446#define MA_DEVICE_NOT_STARTED -202
1447#define MA_DEVICE_UNAVAILABLE -203
1448
1449/* Operation errors. */
1450#define MA_FAILED_TO_MAP_DEVICE_BUFFER -300
1451#define MA_FAILED_TO_UNMAP_DEVICE_BUFFER -301
1452#define MA_FAILED_TO_INIT_BACKEND -302
1453#define MA_FAILED_TO_READ_DATA_FROM_CLIENT -303
1454#define MA_FAILED_TO_READ_DATA_FROM_DEVICE -304
1455#define MA_FAILED_TO_SEND_DATA_TO_CLIENT -305
1456#define MA_FAILED_TO_SEND_DATA_TO_DEVICE -306
1457#define MA_FAILED_TO_OPEN_BACKEND_DEVICE -307
1458#define MA_FAILED_TO_START_BACKEND_DEVICE -308
1459#define MA_FAILED_TO_STOP_BACKEND_DEVICE -309
1460#define MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE -310
1461#define MA_FAILED_TO_CREATE_MUTEX -311
1462#define MA_FAILED_TO_CREATE_EVENT -312
1463#define MA_FAILED_TO_CREATE_SEMAPHORE -313
1464#define MA_FAILED_TO_CREATE_THREAD -314
1465
1466
1467/* Standard sample rates. */
1468#define MA_SAMPLE_RATE_8000 8000
1469#define MA_SAMPLE_RATE_11025 11025
1470#define MA_SAMPLE_RATE_16000 16000
1471#define MA_SAMPLE_RATE_22050 22050
1472#define MA_SAMPLE_RATE_24000 24000
1473#define MA_SAMPLE_RATE_32000 32000
1474#define MA_SAMPLE_RATE_44100 44100
1475#define MA_SAMPLE_RATE_48000 48000
1476#define MA_SAMPLE_RATE_88200 88200
1477#define MA_SAMPLE_RATE_96000 96000
1478#define MA_SAMPLE_RATE_176400 176400
1479#define MA_SAMPLE_RATE_192000 192000
1480#define MA_SAMPLE_RATE_352800 352800
1481#define MA_SAMPLE_RATE_384000 384000
1482
1483#define MA_MIN_CHANNELS 1
1484#define MA_MAX_CHANNELS 32
1485#define MA_MIN_SAMPLE_RATE MA_SAMPLE_RATE_8000
1486#define MA_MAX_SAMPLE_RATE MA_SAMPLE_RATE_384000
1487
1488typedef enum
1489{
1490 ma_stream_format_pcm = 0
1491} ma_stream_format;
1492
1493typedef enum
1494{
1495 ma_stream_layout_interleaved = 0,
1496 ma_stream_layout_deinterleaved
1497} ma_stream_layout;
1498
1499typedef enum
1500{
1501 ma_dither_mode_none = 0,
1502 ma_dither_mode_rectangle,
1503 ma_dither_mode_triangle
1504} ma_dither_mode;
1505
1506typedef enum
1507{
1508 /*
1509 I like to keep these explicitly defined because they're used as a key into a lookup table. When items are
1510 added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample().
1511 */
1512 ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */
1513 ma_format_u8 = 1,
1514 ma_format_s16 = 2, /* Seems to be the most widely supported format. */
1515 ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */
1516 ma_format_s32 = 4,
1517 ma_format_f32 = 5,
1518 ma_format_count
1519} ma_format;
1520
1521typedef enum
1522{
1523 ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */
1524 ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */
1525 ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_router_config. */
1526 ma_channel_mix_mode_planar_blend = ma_channel_mix_mode_rectangular,
1527 ma_channel_mix_mode_default = ma_channel_mix_mode_planar_blend
1528} ma_channel_mix_mode;
1529
1530typedef enum
1531{
1532 ma_standard_channel_map_microsoft,
1533 ma_standard_channel_map_alsa,
1534 ma_standard_channel_map_rfc3551, /* Based off AIFF. */
1535 ma_standard_channel_map_flac,
1536 ma_standard_channel_map_vorbis,
1537 ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */
1538 ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */
1539 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. */
1540 ma_standard_channel_map_default = ma_standard_channel_map_microsoft
1541} ma_standard_channel_map;
1542
1543typedef enum
1544{
1545 ma_performance_profile_low_latency = 0,
1546 ma_performance_profile_conservative
1547} ma_performance_profile;
1548
1549
1550typedef struct
1551{
1552 void* pUserData;
1553 void* (* onMalloc)(size_t sz, void* pUserData);
1554 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
1555 void (* onFree)(void* p, void* pUserData);
1556} ma_allocation_callbacks;
1557
1558
1559/**************************************************************************************************************************************************************
1560
1561Biquad Filtering
1562
1563**************************************************************************************************************************************************************/
1564typedef union
1565{
1566 float f32;
1567 ma_int32 s32;
1568} ma_biquad_coefficient;
1569
1570typedef struct
1571{
1572 ma_format format;
1573 ma_uint32 channels;
1574 double b0;
1575 double b1;
1576 double b2;
1577 double a0;
1578 double a1;
1579 double a2;
1580} ma_biquad_config;
1581
1582ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2);
1583
1584typedef struct
1585{
1586 ma_format format;
1587 ma_uint32 channels;
1588 ma_biquad_coefficient b0;
1589 ma_biquad_coefficient b1;
1590 ma_biquad_coefficient b2;
1591 ma_biquad_coefficient a1;
1592 ma_biquad_coefficient a2;
1593 ma_biquad_coefficient r1[MA_MAX_CHANNELS];
1594 ma_biquad_coefficient r2[MA_MAX_CHANNELS];
1595} ma_biquad;
1596
1597ma_result ma_biquad_init(const ma_biquad_config* pConfig, ma_biquad* pBQ);
1598ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ);
1599ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
1600ma_uint32 ma_biquad_get_latency(ma_biquad* pBQ);
1601
1602
1603/**************************************************************************************************************************************************************
1604
1605Low-Pass Filtering
1606
1607**************************************************************************************************************************************************************/
1608typedef struct
1609{
1610 ma_format format;
1611 ma_uint32 channels;
1612 ma_uint32 sampleRate;
1613 double cutoffFrequency;
1614} ma_lpf_config;
1615
1616ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency);
1617
1618typedef struct
1619{
1620 ma_biquad bq; /* The low-pass filter is implemented as a biquad filter. */
1621} ma_lpf;
1622
1623ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF);
1624ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF);
1625ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
1626ma_uint32 ma_lpf_get_latency(ma_lpf* pLPF);
1627
1628
1629/**************************************************************************************************************************************************************
1630
1631High-Pass Filtering
1632
1633**************************************************************************************************************************************************************/
1634typedef struct
1635{
1636 ma_format format;
1637 ma_uint32 channels;
1638 ma_uint32 sampleRate;
1639 double cutoffFrequency;
1640} ma_hpf_config;
1641
1642ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency);
1643
1644typedef struct
1645{
1646 ma_biquad bq; /* The high-pass filter is implemented as a biquad filter. */
1647} ma_hpf;
1648
1649ma_result ma_hpf_init(const ma_hpf_config* pConfig, ma_hpf* pHPF);
1650ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF);
1651ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
1652ma_uint32 ma_hpf_get_latency(ma_hpf* pHPF);
1653
1654
1655/**************************************************************************************************************************************************************
1656
1657Band-Pass Filtering
1658
1659**************************************************************************************************************************************************************/
1660typedef struct
1661{
1662 ma_format format;
1663 ma_uint32 channels;
1664 ma_uint32 sampleRate;
1665 double cutoffFrequency;
1666} ma_bpf_config;
1667
1668ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency);
1669
1670typedef struct
1671{
1672 ma_biquad bq; /* The band-pass filter is implemented as a biquad filter. */
1673} ma_bpf;
1674
1675ma_result ma_bpf_init(const ma_bpf_config* pConfig, ma_bpf* pBPF);
1676ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF);
1677ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
1678ma_uint32 ma_bpf_get_latency(ma_bpf* pBPF);
1679
1680
1681/************************************************************************************************************************************************************
1682*************************************************************************************************************************************************************
1683
1684DATA CONVERSION
1685===============
1686
1687This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc.
1688
1689*************************************************************************************************************************************************************
1690************************************************************************************************************************************************************/
1691
1692/**************************************************************************************************************************************************************
1693
1694Resampling
1695
1696**************************************************************************************************************************************************************/
1697#ifndef MA_MAX_RESAMPLER_LPF_FILTERS
1698#define MA_MAX_RESAMPLER_LPF_FILTERS 4
1699#endif
1700
1701typedef struct
1702{
1703 ma_format format;
1704 ma_uint32 channels;
1705 ma_uint32 sampleRateIn;
1706 ma_uint32 sampleRateOut;
1707 ma_uint32 lpfCount; /* How many low-pass filters to chain together. A single low-pass filter is second order. Setting this to 0 will disable low-pass filtering. */
1708 double lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */
1709} ma_linear_resampler_config;
1710
1711ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
1712
1713typedef struct
1714{
1715 ma_linear_resampler_config config;
1716 ma_uint32 inAdvanceInt;
1717 ma_uint32 inAdvanceFrac;
1718 ma_uint32 inTimeInt;
1719 ma_uint32 inTimeFrac;
1720 union
1721 {
1722 float f32[MA_MAX_CHANNELS];
1723 ma_int16 s16[MA_MAX_CHANNELS];
1724 } x0; /* The previous input frame. */
1725 union
1726 {
1727 float f32[MA_MAX_CHANNELS];
1728 ma_int16 s16[MA_MAX_CHANNELS];
1729 } x1; /* The next input frame. */
1730 ma_lpf lpf[MA_MAX_RESAMPLER_LPF_FILTERS];
1731} ma_linear_resampler;
1732
1733ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, ma_linear_resampler* pResampler);
1734void ma_linear_resampler_uninit(ma_linear_resampler* pResampler);
1735ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
1736ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
1737ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut);
1738ma_uint64 ma_linear_resampler_get_required_input_frame_count(ma_linear_resampler* pResampler, ma_uint64 outputFrameCount);
1739ma_uint64 ma_linear_resampler_get_expected_output_frame_count(ma_linear_resampler* pResampler, ma_uint64 inputFrameCount);
1740ma_uint64 ma_linear_resampler_get_input_latency(ma_linear_resampler* pResampler);
1741ma_uint64 ma_linear_resampler_get_output_latency(ma_linear_resampler* pResampler);
1742
1743typedef enum
1744{
1745 ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */
1746 ma_resample_algorithm_speex
1747} ma_resample_algorithm;
1748
1749typedef struct
1750{
1751 ma_format format; /* Must be either ma_format_f32 or ma_format_s16. */
1752 ma_uint32 channels;
1753 ma_uint32 sampleRateIn;
1754 ma_uint32 sampleRateOut;
1755 ma_resample_algorithm algorithm;
1756 struct
1757 {
1758 ma_uint32 lpfCount;
1759 double lpfNyquistFactor;
1760 } linear;
1761 struct
1762 {
1763 int quality; /* 0 to 10. Defaults to 3. */
1764 } speex;
1765} ma_resampler_config;
1766
1767ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm);
1768
1769typedef struct
1770{
1771 ma_resampler_config config;
1772 union
1773 {
1774 ma_linear_resampler linear;
1775 struct
1776 {
1777 void* pSpeexResamplerState; /* SpeexResamplerState* */
1778 } speex;
1779 } state;
1780} ma_resampler;
1781
1782/*
1783Initializes a new resampler object from a config.
1784*/
1785ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pResampler);
1786
1787/*
1788Uninitializes a resampler.
1789*/
1790void ma_resampler_uninit(ma_resampler* pResampler);
1791
1792/*
1793Converts the given input data.
1794
1795Both the input and output frames must be in the format specified in the config when the resampler was initilized.
1796
1797On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that
1798were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use
1799ma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames.
1800
1801On input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole
1802input frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames
1803you should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead.
1804
1805If [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of
1806output frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input
1807frames. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be
1808processed. In this case, any internal filter state will be updated as if zeroes were passed in.
1809
1810It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL.
1811
1812It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL.
1813*/
1814ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
1815
1816
1817/*
1818Sets the input and output sample sample rate.
1819*/
1820ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
1821
1822/*
1823Sets the input and output sample rate as a ratio.
1824
1825The ration is in/out.
1826*/
1827ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio);
1828
1829
1830/*
1831Calculates the number of whole input frames that would need to be read from the client in order to output the specified
1832number of output frames.
1833
1834The returned value does not include cached input frames. It only returns the number of extra frames that would need to be
1835read from the input buffer in order to output the specified number of output frames.
1836*/
1837ma_uint64 ma_resampler_get_required_input_frame_count(ma_resampler* pResampler, ma_uint64 outputFrameCount);
1838
1839/*
1840Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of
1841input frames.
1842*/
1843ma_uint64 ma_resampler_get_expected_output_frame_count(ma_resampler* pResampler, ma_uint64 inputFrameCount);
1844
1845
1846/*
1847Retrieves the latency introduced by the resampler in input frames.
1848*/
1849ma_uint64 ma_resampler_get_input_latency(ma_resampler* pResampler);
1850
1851/*
1852Retrieves the latency introduced by the resampler in output frames.
1853*/
1854ma_uint64 ma_resampler_get_output_latency(ma_resampler* pResampler);
1855
1856
1857
1858/**************************************************************************************************************************************************************
1859
1860Channel Conversion
1861
1862**************************************************************************************************************************************************************/
1863typedef struct
1864{
1865 ma_format format;
1866 ma_uint32 channelsIn;
1867 ma_uint32 channelsOut;
1868 ma_channel channelMapIn[MA_MAX_CHANNELS];
1869 ma_channel channelMapOut[MA_MAX_CHANNELS];
1870 ma_channel_mix_mode mixingMode;
1871 float weights[MA_MAX_CHANNELS][MA_MAX_CHANNELS]; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */
1872} ma_channel_converter_config;
1873
1874ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel channelMapIn[MA_MAX_CHANNELS], ma_uint32 channelsOut, const ma_channel channelMapOut[MA_MAX_CHANNELS], ma_channel_mix_mode mixingMode);
1875
1876typedef struct
1877{
1878 ma_format format;
1879 ma_uint32 channelsIn;
1880 ma_uint32 channelsOut;
1881 ma_channel channelMapIn[MA_MAX_CHANNELS];
1882 ma_channel channelMapOut[MA_MAX_CHANNELS];
1883 ma_channel_mix_mode mixingMode;
1884 union
1885 {
1886 float f32[MA_MAX_CHANNELS][MA_MAX_CHANNELS];
1887 ma_int32 s16[MA_MAX_CHANNELS][MA_MAX_CHANNELS];
1888 } weights;
1889 ma_bool32 isPassthrough : 1;
1890 ma_bool32 isSimpleShuffle : 1;
1891 ma_bool32 isSimpleMonoExpansion : 1;
1892 ma_bool32 isStereoToMono : 1;
1893 ma_uint8 shuffleTable[MA_MAX_CHANNELS];
1894} ma_channel_converter;
1895
1896ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, ma_channel_converter* pConverter);
1897void ma_channel_converter_uninit(ma_channel_converter* pConverter);
1898ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
1899
1900
1901/**************************************************************************************************************************************************************
1902
1903Data Conversion
1904
1905**************************************************************************************************************************************************************/
1906typedef struct
1907{
1908 ma_format formatIn;
1909 ma_format formatOut;
1910 ma_uint32 channelsIn;
1911 ma_uint32 channelsOut;
1912 ma_uint32 sampleRateIn;
1913 ma_uint32 sampleRateOut;
1914 ma_channel channelMapIn[MA_MAX_CHANNELS];
1915 ma_channel channelMapOut[MA_MAX_CHANNELS];
1916 ma_dither_mode ditherMode;
1917 ma_channel_mix_mode channelMixMode;
1918 float channelWeights[MA_MAX_CHANNELS][MA_MAX_CHANNELS]; /* [in][out]. Only used when channelMixMode is set to ma_channel_mix_mode_custom_weights. */
1919 struct
1920 {
1921 ma_resample_algorithm algorithm;
1922 ma_bool32 allowDynamicSampleRate;
1923 struct
1924 {
1925 ma_uint32 lpfCount;
1926 double lpfNyquistFactor;
1927 } linear;
1928 struct
1929 {
1930 int quality;
1931 } speex;
1932 } resampling;
1933} ma_data_converter_config;
1934
1935ma_data_converter_config ma_data_converter_config_init_default(void);
1936ma_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);
1937
1938typedef struct
1939{
1940 ma_data_converter_config config;
1941 ma_channel_converter channelConverter;
1942 ma_resampler resampler;
1943 ma_bool32 hasPreFormatConversion : 1;
1944 ma_bool32 hasPostFormatConversion : 1;
1945 ma_bool32 hasChannelConverter : 1;
1946 ma_bool32 hasResampler : 1;
1947 ma_bool32 isPassthrough : 1;
1948} ma_data_converter;
1949
1950ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, ma_data_converter* pConverter);
1951void ma_data_converter_uninit(ma_data_converter* pConverter);
1952ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
1953ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
1954ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut);
1955ma_uint64 ma_data_converter_get_required_input_frame_count(ma_data_converter* pConverter, ma_uint64 outputFrameCount);
1956ma_uint64 ma_data_converter_get_expected_output_frame_count(ma_data_converter* pConverter, ma_uint64 inputFrameCount);
1957ma_uint64 ma_data_converter_get_input_latency(ma_data_converter* pConverter);
1958ma_uint64 ma_data_converter_get_output_latency(ma_data_converter* pConverter);
1959
1960
1961/************************************************************************************************************************************************************
1962
1963Format Conversion
1964
1965************************************************************************************************************************************************************/
1966void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1967void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1968void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1969void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1970void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1971void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1972void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1973void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1974void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1975void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1976void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1977void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1978void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1979void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1980void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1981void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1982void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1983void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1984void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1985void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1986void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode);
1987void 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);
1988
1989/*
1990Deinterleaves an interleaved buffer.
1991*/
1992void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames);
1993
1994/*
1995Interleaves a group of deinterleaved buffers.
1996*/
1997void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames);
1998
1999/************************************************************************************************************************************************************
2000
2001Channel Maps
2002
2003************************************************************************************************************************************************************/
2004
2005/*
2006Helper for retrieving a standard channel map.
2007*/
2008void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS]);
2009
2010/*
2011Copies a channel map.
2012*/
2013void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels);
2014
2015
2016/*
2017Determines whether or not a channel map is valid.
2018
2019A blank channel map is valid (all channels set to MA_CHANNEL_NONE). The way a blank channel map is handled is context specific, but
2020is usually treated as a passthrough.
2021
2022Invalid channel maps:
2023 - A channel map with no channels
2024 - A channel map with more than one channel and a mono channel
2025*/
2026ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS]);
2027
2028/*
2029Helper for comparing two channel maps for equality.
2030
2031This assumes the channel count is the same between the two.
2032*/
2033ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel channelMapA[MA_MAX_CHANNELS], const ma_channel channelMapB[MA_MAX_CHANNELS]);
2034
2035/*
2036Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE).
2037*/
2038ma_bool32 ma_channel_map_blank(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS]);
2039
2040/*
2041Helper for determining whether or not a channel is present in the given channel map.
2042*/
2043ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS], ma_channel channelPosition);
2044
2045
2046/************************************************************************************************************************************************************
2047
2048Conversion Helpers
2049
2050************************************************************************************************************************************************************/
2051
2052/*
2053High-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
2054determine the required size of the output buffer. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is
2055ignored.
2056
2057A return value of 0 indicates an error.
2058
2059This function is useful for one-off bulk conversions, but if you're streaming data you should use the ma_data_converter APIs instead.
2060*/
2061ma_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);
2062ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig);
2063
2064
2065/************************************************************************************************************************************************************
2066
2067Ring Buffer
2068
2069************************************************************************************************************************************************************/
2070typedef struct
2071{
2072 void* pBuffer;
2073 ma_uint32 subbufferSizeInBytes;
2074 ma_uint32 subbufferCount;
2075 ma_uint32 subbufferStrideInBytes;
2076 volatile ma_uint32 encodedReadOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. */
2077 volatile ma_uint32 encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. */
2078 ma_bool32 ownsBuffer : 1; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */
2079 ma_bool32 clearOnWriteAcquire : 1; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */
2080 ma_allocation_callbacks allocationCallbacks;
2081} ma_rb;
2082
2083ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB);
2084ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB);
2085void ma_rb_uninit(ma_rb* pRB);
2086void ma_rb_reset(ma_rb* pRB);
2087ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
2088ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut);
2089ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
2090ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut);
2091ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes);
2092ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes);
2093ma_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. */
2094ma_uint32 ma_rb_available_read(ma_rb* pRB);
2095ma_uint32 ma_rb_available_write(ma_rb* pRB);
2096size_t ma_rb_get_subbuffer_size(ma_rb* pRB);
2097size_t ma_rb_get_subbuffer_stride(ma_rb* pRB);
2098size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex);
2099void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer);
2100
2101
2102typedef struct
2103{
2104 ma_rb rb;
2105 ma_format format;
2106 ma_uint32 channels;
2107} ma_pcm_rb;
2108
2109ma_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);
2110ma_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);
2111void ma_pcm_rb_uninit(ma_pcm_rb* pRB);
2112void ma_pcm_rb_reset(ma_pcm_rb* pRB);
2113ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
2114ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut);
2115ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
2116ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut);
2117ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames);
2118ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames);
2119ma_int32 ma_pcm_rb_pointer_disance(ma_pcm_rb* pRB); /* Return value is in frames. */
2120ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB);
2121ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB);
2122ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB);
2123ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB);
2124ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex);
2125void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer);
2126
2127
2128/************************************************************************************************************************************************************
2129
2130Miscellaneous Helpers
2131
2132************************************************************************************************************************************************************/
2133
2134/*
2135malloc(). Calls MA_MALLOC().
2136*/
2137void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
2138
2139/*
2140realloc(). Calls MA_REALLOC().
2141*/
2142void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
2143
2144/*
2145free(). Calls MA_FREE().
2146*/
2147void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
2148
2149/*
2150Performs an aligned malloc, with the assumption that the alignment is a power of 2.
2151*/
2152void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks);
2153
2154/*
2155Free's an aligned malloc'd buffer.
2156*/
2157void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
2158
2159/*
2160Retrieves a friendly name for a format.
2161*/
2162const char* ma_get_format_name(ma_format format);
2163
2164/*
2165Blends two frames in floating point format.
2166*/
2167void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels);
2168
2169/*
2170Retrieves the size of a sample in bytes for the given format.
2171
2172This API is efficient and is implemented using a lookup table.
2173
2174Thread Safety: SAFE
2175 This API is pure.
2176*/
2177ma_uint32 ma_get_bytes_per_sample(ma_format format);
2178static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; }
2179
2180/*
2181Converts a log level to a string.
2182*/
2183const char* ma_log_level_to_string(ma_uint32 logLevel);
2184
2185
2186
2187/************************************************************************************************************************************************************
2188*************************************************************************************************************************************************************
2189
2190DEVICE I/O
2191==========
2192
2193This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc.
2194
2195*************************************************************************************************************************************************************
2196************************************************************************************************************************************************************/
2197#ifndef MA_NO_DEVICE_IO
2198/* Some backends are only supported on certain platforms. */
2199#if defined(MA_WIN32)
2200 #define MA_SUPPORT_WASAPI
2201 #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */
2202 #define MA_SUPPORT_DSOUND
2203 #define MA_SUPPORT_WINMM
2204 #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */
2205 #endif
2206#endif
2207#if defined(MA_UNIX)
2208 #if defined(MA_LINUX)
2209 #if !defined(MA_ANDROID) /* ALSA is not supported on Android. */
2210 #define MA_SUPPORT_ALSA
2211 #endif
2212 #endif
2213 #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN)
2214 #define MA_SUPPORT_PULSEAUDIO
2215 #define MA_SUPPORT_JACK
2216 #endif
2217 #if defined(MA_ANDROID)
2218 #define MA_SUPPORT_AAUDIO
2219 #define MA_SUPPORT_OPENSL
2220 #endif
2221 #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */
2222 #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */
2223 #endif
2224 #if defined(__NetBSD__) || defined(__OpenBSD__)
2225 #define MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */
2226 #endif
2227 #if defined(__FreeBSD__) || defined(__DragonFly__)
2228 #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */
2229 #endif
2230#endif
2231#if defined(MA_APPLE)
2232 #define MA_SUPPORT_COREAUDIO
2233#endif
2234#if defined(MA_EMSCRIPTEN)
2235 #define MA_SUPPORT_WEBAUDIO
2236#endif
2237
2238/* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */
2239#if !defined(MA_EMSCRIPTEN)
2240#define MA_SUPPORT_NULL
2241#endif
2242
2243
2244#if !defined(MA_NO_WASAPI) && defined(MA_SUPPORT_WASAPI)
2245 #define MA_ENABLE_WASAPI
2246#endif
2247#if !defined(MA_NO_DSOUND) && defined(MA_SUPPORT_DSOUND)
2248 #define MA_ENABLE_DSOUND
2249#endif
2250#if !defined(MA_NO_WINMM) && defined(MA_SUPPORT_WINMM)
2251 #define MA_ENABLE_WINMM
2252#endif
2253#if !defined(MA_NO_ALSA) && defined(MA_SUPPORT_ALSA)
2254 #define MA_ENABLE_ALSA
2255#endif
2256#if !defined(MA_NO_PULSEAUDIO) && defined(MA_SUPPORT_PULSEAUDIO)
2257 #define MA_ENABLE_PULSEAUDIO
2258#endif
2259#if !defined(MA_NO_JACK) && defined(MA_SUPPORT_JACK)
2260 #define MA_ENABLE_JACK
2261#endif
2262#if !defined(MA_NO_COREAUDIO) && defined(MA_SUPPORT_COREAUDIO)
2263 #define MA_ENABLE_COREAUDIO
2264#endif
2265#if !defined(MA_NO_SNDIO) && defined(MA_SUPPORT_SNDIO)
2266 #define MA_ENABLE_SNDIO
2267#endif
2268#if !defined(MA_NO_AUDIO4) && defined(MA_SUPPORT_AUDIO4)
2269 #define MA_ENABLE_AUDIO4
2270#endif
2271#if !defined(MA_NO_OSS) && defined(MA_SUPPORT_OSS)
2272 #define MA_ENABLE_OSS
2273#endif
2274#if !defined(MA_NO_AAUDIO) && defined(MA_SUPPORT_AAUDIO)
2275 #define MA_ENABLE_AAUDIO
2276#endif
2277#if !defined(MA_NO_OPENSL) && defined(MA_SUPPORT_OPENSL)
2278 #define MA_ENABLE_OPENSL
2279#endif
2280#if !defined(MA_NO_WEBAUDIO) && defined(MA_SUPPORT_WEBAUDIO)
2281 #define MA_ENABLE_WEBAUDIO
2282#endif
2283#if !defined(MA_NO_NULL) && defined(MA_SUPPORT_NULL)
2284 #define MA_ENABLE_NULL
2285#endif
2286
2287#ifdef MA_SUPPORT_WASAPI
2288/* We need a IMMNotificationClient object for WASAPI. */
2289typedef struct
2290{
2291 void* lpVtbl;
2292 ma_uint32 counter;
2293 ma_device* pDevice;
2294} ma_IMMNotificationClient;
2295#endif
2296
2297/* Backend enums must be in priority order. */
2298typedef enum
2299{
2300 ma_backend_wasapi,
2301 ma_backend_dsound,
2302 ma_backend_winmm,
2303 ma_backend_coreaudio,
2304 ma_backend_sndio,
2305 ma_backend_audio4,
2306 ma_backend_oss,
2307 ma_backend_pulseaudio,
2308 ma_backend_alsa,
2309 ma_backend_jack,
2310 ma_backend_aaudio,
2311 ma_backend_opensl,
2312 ma_backend_webaudio,
2313 ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */
2314} ma_backend;
2315
2316/* Thread priorties should be ordered such that the default priority of the worker thread is 0. */
2317typedef enum
2318{
2319 ma_thread_priority_idle = -5,
2320 ma_thread_priority_lowest = -4,
2321 ma_thread_priority_low = -3,
2322 ma_thread_priority_normal = -2,
2323 ma_thread_priority_high = -1,
2324 ma_thread_priority_highest = 0,
2325 ma_thread_priority_realtime = 1,
2326 ma_thread_priority_default = 0
2327} ma_thread_priority;
2328
2329typedef struct
2330{
2331 ma_context* pContext;
2332
2333 union
2334 {
2335#ifdef MA_WIN32
2336 struct
2337 {
2338 /*HANDLE*/ ma_handle hThread;
2339 } win32;
2340#endif
2341#ifdef MA_POSIX
2342 struct
2343 {
2344 pthread_t thread;
2345 } posix;
2346#endif
2347 int _unused;
2348 };
2349} ma_thread;
2350
2351typedef struct
2352{
2353 ma_context* pContext;
2354
2355 union
2356 {
2357#ifdef MA_WIN32
2358 struct
2359 {
2360 /*HANDLE*/ ma_handle hMutex;
2361 } win32;
2362#endif
2363#ifdef MA_POSIX
2364 struct
2365 {
2366 pthread_mutex_t mutex;
2367 } posix;
2368#endif
2369 int _unused;
2370 };
2371} ma_mutex;
2372
2373typedef struct
2374{
2375 ma_context* pContext;
2376
2377 union
2378 {
2379#ifdef MA_WIN32
2380 struct
2381 {
2382 /*HANDLE*/ ma_handle hEvent;
2383 } win32;
2384#endif
2385#ifdef MA_POSIX
2386 struct
2387 {
2388 pthread_mutex_t mutex;
2389 pthread_cond_t condition;
2390 ma_uint32 value;
2391 } posix;
2392#endif
2393 int _unused;
2394 };
2395} ma_event;
2396
2397typedef struct
2398{
2399 ma_context* pContext;
2400
2401 union
2402 {
2403#ifdef MA_WIN32
2404 struct
2405 {
2406 /*HANDLE*/ ma_handle hSemaphore;
2407 } win32;
2408#endif
2409#ifdef MA_POSIX
2410 struct
2411 {
2412 sem_t semaphore;
2413 } posix;
2414#endif
2415 int _unused;
2416 };
2417} ma_semaphore;
2418
2419
2420/*
2421The callback for processing audio data from the device.
2422
2423The 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
2424available. 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
2425callback will be fired with a consistent frame count.
2426
2427
2428Parameters
2429----------
2430pDevice (in)
2431 A pointer to the relevant device.
2432
2433pOutput (out)
2434 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
2435 full-duplex device and null for a capture and loopback device.
2436
2437pInput (in)
2438 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
2439 playback device.
2440
2441frameCount (in)
2442 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
2443 `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must
2444 not assume this will always be the same value each time the callback is fired.
2445
2446
2447Remarks
2448-------
2449You 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
2450callback. The following APIs cannot be called from inside the callback:
2451
2452 ma_device_init()
2453 ma_device_init_ex()
2454 ma_device_uninit()
2455 ma_device_start()
2456 ma_device_stop()
2457
2458The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread.
2459*/
2460typedef void (* ma_device_callback_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
2461
2462/*
2463The callback for when the device has been stopped.
2464
2465This 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
2466such as being unplugged or an internal error occuring.
2467
2468
2469Parameters
2470----------
2471pDevice (in)
2472 A pointer to the device that has just stopped.
2473
2474
2475Remarks
2476-------
2477Do not restart or uninitialize the device from the callback.
2478*/
2479typedef void (* ma_stop_proc)(ma_device* pDevice);
2480
2481/*
2482The callback for handling log messages.
2483
2484
2485Parameters
2486----------
2487pContext (in)
2488 A pointer to the context the log message originated from.
2489
2490pDevice (in)
2491 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.
2492
2493logLevel (in)
2494 The log level. This can be one of the following:
2495
2496 |----------------------|
2497 | Log Level |
2498 |----------------------|
2499 | MA_LOG_LEVEL_VERBOSE |
2500 | MA_LOG_LEVEL_INFO |
2501 | MA_LOG_LEVEL_WARNING |
2502 | MA_LOG_LEVEL_ERROR |
2503 |----------------------|
2504
2505message (in)
2506 The log message.
2507
2508
2509Remarks
2510-------
2511Do not modify the state of the device from inside the callback.
2512*/
2513typedef void (* ma_log_proc)(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message);
2514
2515typedef enum
2516{
2517 ma_device_type_playback = 1,
2518 ma_device_type_capture = 2,
2519 ma_device_type_duplex = ma_device_type_playback | ma_device_type_capture, /* 3 */
2520 ma_device_type_loopback = 4
2521} ma_device_type;
2522
2523typedef enum
2524{
2525 ma_share_mode_shared = 0,
2526 ma_share_mode_exclusive
2527} ma_share_mode;
2528
2529/* iOS/tvOS/watchOS session categories. */
2530typedef enum
2531{
2532 ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord with AVAudioSessionCategoryOptionDefaultToSpeaker. */
2533 ma_ios_session_category_none, /* Leave the session category unchanged. */
2534 ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */
2535 ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */
2536 ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */
2537 ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */
2538 ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */
2539 ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */
2540} ma_ios_session_category;
2541
2542/* iOS/tvOS/watchOS session category options */
2543typedef enum
2544{
2545 ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */
2546 ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */
2547 ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */
2548 ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */
2549 ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */
2550 ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */
2551 ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */
2552} ma_ios_session_category_option;
2553
2554typedef union
2555{
2556 ma_int64 counter;
2557 double counterD;
2558} ma_timer;
2559
2560typedef union
2561{
2562 wchar_t wasapi[64]; /* WASAPI uses a wchar_t string for identification. */
2563 ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */
2564 /*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. */
2565 char alsa[256]; /* ALSA uses a name string for identification. */
2566 char pulse[256]; /* PulseAudio uses a name string for identification. */
2567 int jack; /* JACK always uses default devices. */
2568 char coreaudio[256]; /* Core Audio uses a string for identification. */
2569 char sndio[256]; /* "snd/0", etc. */
2570 char audio4[256]; /* "/dev/audio", etc. */
2571 char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */
2572 ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */
2573 ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */
2574 char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */
2575 int nullbackend; /* The null backend uses an integer for device IDs. */
2576} ma_device_id;
2577
2578typedef struct
2579{
2580 /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */
2581 ma_device_id id;
2582 char name[256];
2583
2584 /*
2585 Detailed info. As much of this is filled as possible with ma_context_get_device_info(). Note that you are allowed to initialize
2586 a device with settings outside of this range, but it just means the data will be converted using miniaudio's data conversion
2587 pipeline before sending the data to/from the device. Most programs will need to not worry about these values, but it's provided
2588 here mainly for informational purposes or in the rare case that someone might find it useful.
2589
2590 These will be set to 0 when returned by ma_context_enumerate_devices() or ma_context_get_devices().
2591 */
2592 ma_uint32 formatCount;
2593 ma_format formats[ma_format_count];
2594 ma_uint32 minChannels;
2595 ma_uint32 maxChannels;
2596 ma_uint32 minSampleRate;
2597 ma_uint32 maxSampleRate;
2598
2599 struct
2600 {
2601 ma_bool32 isDefault;
2602 } _private;
2603} ma_device_info;
2604
2605typedef struct
2606{
2607 ma_device_type deviceType;
2608 ma_uint32 sampleRate;
2609 ma_uint32 periodSizeInFrames;
2610 ma_uint32 periodSizeInMilliseconds;
2611 ma_uint32 periods;
2612 ma_performance_profile performanceProfile;
2613 ma_bool32 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. */
2614 ma_bool32 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. */
2615 ma_device_callback_proc dataCallback;
2616 ma_stop_proc stopCallback;
2617 void* pUserData;
2618 struct
2619 {
2620 ma_resample_algorithm algorithm;
2621 struct
2622 {
2623 ma_uint32 lpfCount;
2624 } linear;
2625 struct
2626 {
2627 int quality;
2628 } speex;
2629 } resampling;
2630 struct
2631 {
2632 ma_device_id* pDeviceID;
2633 ma_format format;
2634 ma_uint32 channels;
2635 ma_channel channelMap[MA_MAX_CHANNELS];
2636 ma_share_mode shareMode;
2637 } playback;
2638 struct
2639 {
2640 ma_device_id* pDeviceID;
2641 ma_format format;
2642 ma_uint32 channels;
2643 ma_channel channelMap[MA_MAX_CHANNELS];
2644 ma_share_mode shareMode;
2645 } capture;
2646
2647 struct
2648 {
2649 ma_bool32 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
2650 ma_bool32 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
2651 ma_bool32 noAutoStreamRouting; /* Disables automatic stream routing. */
2652 ma_bool32 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */
2653 } wasapi;
2654 struct
2655 {
2656 ma_bool32 noMMap; /* Disables MMap mode. */
2657 } alsa;
2658 struct
2659 {
2660 const char* pStreamNamePlayback;
2661 const char* pStreamNameCapture;
2662 } pulse;
2663} ma_device_config;
2664
2665typedef struct
2666{
2667 ma_log_proc logCallback;
2668 ma_thread_priority threadPriority;
2669 void* pUserData;
2670 ma_allocation_callbacks allocationCallbacks;
2671 struct
2672 {
2673 ma_bool32 useVerboseDeviceEnumeration;
2674 } alsa;
2675 struct
2676 {
2677 const char* pApplicationName;
2678 const char* pServerName;
2679 ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */
2680 } pulse;
2681 struct
2682 {
2683 ma_ios_session_category sessionCategory;
2684 ma_uint32 sessionCategoryOptions;
2685 } coreaudio;
2686 struct
2687 {
2688 const char* pClientName;
2689 ma_bool32 tryStartServer;
2690 } jack;
2691} ma_context_config;
2692
2693/*
2694The callback for handling device enumeration. This is fired from `ma_context_enumerated_devices()`.
2695
2696
2697Parameters
2698----------
2699pContext (in)
2700 A pointer to the context performing the enumeration.
2701
2702deviceType (in)
2703 The type of the device being enumerated. This will always be either `ma_device_type_playback` or `ma_device_type_capture`.
2704
2705pInfo (in)
2706 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,
2707 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
2708 is too inefficient.
2709
2710pUserData (in)
2711 The user data pointer passed into `ma_context_enumerate_devices()`.
2712*/
2713typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData);
2714
2715struct ma_context
2716{
2717 ma_backend backend; /* DirectSound, ALSA, etc. */
2718 ma_log_proc logCallback;
2719 ma_thread_priority threadPriority;
2720 void* pUserData;
2721 ma_allocation_callbacks allocationCallbacks;
2722 ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */
2723 ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */
2724 ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */
2725 ma_uint32 playbackDeviceInfoCount;
2726 ma_uint32 captureDeviceInfoCount;
2727 ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */
2728 ma_bool32 isBackendAsynchronous : 1; /* Set when the context is initialized. Set to 1 for asynchronous backends such as Core Audio and JACK. Do not modify. */
2729
2730 ma_result (* onUninit )(ma_context* pContext);
2731 ma_bool32 (* onDeviceIDEqual )(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1);
2732 ma_result (* onEnumDevices )(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); /* Return false from the callback to stop enumeration. */
2733 ma_result (* onGetDeviceInfo )(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo);
2734 ma_result (* onDeviceInit )(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice);
2735 void (* onDeviceUninit )(ma_device* pDevice);
2736 ma_result (* onDeviceStart )(ma_device* pDevice);
2737 ma_result (* onDeviceStop )(ma_device* pDevice);
2738 ma_result (* onDeviceMainLoop)(ma_device* pDevice);
2739
2740 union
2741 {
2742#ifdef MA_SUPPORT_WASAPI
2743 struct
2744 {
2745 int _unused;
2746 } wasapi;
2747#endif
2748#ifdef MA_SUPPORT_DSOUND
2749 struct
2750 {
2751 ma_handle hDSoundDLL;
2752 ma_proc DirectSoundCreate;
2753 ma_proc DirectSoundEnumerateA;
2754 ma_proc DirectSoundCaptureCreate;
2755 ma_proc DirectSoundCaptureEnumerateA;
2756 } dsound;
2757#endif
2758#ifdef MA_SUPPORT_WINMM
2759 struct
2760 {
2761 ma_handle hWinMM;
2762 ma_proc waveOutGetNumDevs;
2763 ma_proc waveOutGetDevCapsA;
2764 ma_proc waveOutOpen;
2765 ma_proc waveOutClose;
2766 ma_proc waveOutPrepareHeader;
2767 ma_proc waveOutUnprepareHeader;
2768 ma_proc waveOutWrite;
2769 ma_proc waveOutReset;
2770 ma_proc waveInGetNumDevs;
2771 ma_proc waveInGetDevCapsA;
2772 ma_proc waveInOpen;
2773 ma_proc waveInClose;
2774 ma_proc waveInPrepareHeader;
2775 ma_proc waveInUnprepareHeader;
2776 ma_proc waveInAddBuffer;
2777 ma_proc waveInStart;
2778 ma_proc waveInReset;
2779 } winmm;
2780#endif
2781#ifdef MA_SUPPORT_ALSA
2782 struct
2783 {
2784 ma_handle asoundSO;
2785 ma_proc snd_pcm_open;
2786 ma_proc snd_pcm_close;
2787 ma_proc snd_pcm_hw_params_sizeof;
2788 ma_proc snd_pcm_hw_params_any;
2789 ma_proc snd_pcm_hw_params_set_format;
2790 ma_proc snd_pcm_hw_params_set_format_first;
2791 ma_proc snd_pcm_hw_params_get_format_mask;
2792 ma_proc snd_pcm_hw_params_set_channels_near;
2793 ma_proc snd_pcm_hw_params_set_rate_resample;
2794 ma_proc snd_pcm_hw_params_set_rate_near;
2795 ma_proc snd_pcm_hw_params_set_buffer_size_near;
2796 ma_proc snd_pcm_hw_params_set_periods_near;
2797 ma_proc snd_pcm_hw_params_set_access;
2798 ma_proc snd_pcm_hw_params_get_format;
2799 ma_proc snd_pcm_hw_params_get_channels;
2800 ma_proc snd_pcm_hw_params_get_channels_min;
2801 ma_proc snd_pcm_hw_params_get_channels_max;
2802 ma_proc snd_pcm_hw_params_get_rate;
2803 ma_proc snd_pcm_hw_params_get_rate_min;
2804 ma_proc snd_pcm_hw_params_get_rate_max;
2805 ma_proc snd_pcm_hw_params_get_buffer_size;
2806 ma_proc snd_pcm_hw_params_get_periods;
2807 ma_proc snd_pcm_hw_params_get_access;
2808 ma_proc snd_pcm_hw_params;
2809 ma_proc snd_pcm_sw_params_sizeof;
2810 ma_proc snd_pcm_sw_params_current;
2811 ma_proc snd_pcm_sw_params_get_boundary;
2812 ma_proc snd_pcm_sw_params_set_avail_min;
2813 ma_proc snd_pcm_sw_params_set_start_threshold;
2814 ma_proc snd_pcm_sw_params_set_stop_threshold;
2815 ma_proc snd_pcm_sw_params;
2816 ma_proc snd_pcm_format_mask_sizeof;
2817 ma_proc snd_pcm_format_mask_test;
2818 ma_proc snd_pcm_get_chmap;
2819 ma_proc snd_pcm_state;
2820 ma_proc snd_pcm_prepare;
2821 ma_proc snd_pcm_start;
2822 ma_proc snd_pcm_drop;
2823 ma_proc snd_pcm_drain;
2824 ma_proc snd_device_name_hint;
2825 ma_proc snd_device_name_get_hint;
2826 ma_proc snd_card_get_index;
2827 ma_proc snd_device_name_free_hint;
2828 ma_proc snd_pcm_mmap_begin;
2829 ma_proc snd_pcm_mmap_commit;
2830 ma_proc snd_pcm_recover;
2831 ma_proc snd_pcm_readi;
2832 ma_proc snd_pcm_writei;
2833 ma_proc snd_pcm_avail;
2834 ma_proc snd_pcm_avail_update;
2835 ma_proc snd_pcm_wait;
2836 ma_proc snd_pcm_info;
2837 ma_proc snd_pcm_info_sizeof;
2838 ma_proc snd_pcm_info_get_name;
2839 ma_proc snd_config_update_free_global;
2840
2841 ma_mutex internalDeviceEnumLock;
2842 ma_bool32 useVerboseDeviceEnumeration;
2843 } alsa;
2844#endif
2845#ifdef MA_SUPPORT_PULSEAUDIO
2846 struct
2847 {
2848 ma_handle pulseSO;
2849 ma_proc pa_mainloop_new;
2850 ma_proc pa_mainloop_free;
2851 ma_proc pa_mainloop_get_api;
2852 ma_proc pa_mainloop_iterate;
2853 ma_proc pa_mainloop_wakeup;
2854 ma_proc pa_context_new;
2855 ma_proc pa_context_unref;
2856 ma_proc pa_context_connect;
2857 ma_proc pa_context_disconnect;
2858 ma_proc pa_context_set_state_callback;
2859 ma_proc pa_context_get_state;
2860 ma_proc pa_context_get_sink_info_list;
2861 ma_proc pa_context_get_source_info_list;
2862 ma_proc pa_context_get_sink_info_by_name;
2863 ma_proc pa_context_get_source_info_by_name;
2864 ma_proc pa_operation_unref;
2865 ma_proc pa_operation_get_state;
2866 ma_proc pa_channel_map_init_extend;
2867 ma_proc pa_channel_map_valid;
2868 ma_proc pa_channel_map_compatible;
2869 ma_proc pa_stream_new;
2870 ma_proc pa_stream_unref;
2871 ma_proc pa_stream_connect_playback;
2872 ma_proc pa_stream_connect_record;
2873 ma_proc pa_stream_disconnect;
2874 ma_proc pa_stream_get_state;
2875 ma_proc pa_stream_get_sample_spec;
2876 ma_proc pa_stream_get_channel_map;
2877 ma_proc pa_stream_get_buffer_attr;
2878 ma_proc pa_stream_set_buffer_attr;
2879 ma_proc pa_stream_get_device_name;
2880 ma_proc pa_stream_set_write_callback;
2881 ma_proc pa_stream_set_read_callback;
2882 ma_proc pa_stream_flush;
2883 ma_proc pa_stream_drain;
2884 ma_proc pa_stream_is_corked;
2885 ma_proc pa_stream_cork;
2886 ma_proc pa_stream_trigger;
2887 ma_proc pa_stream_begin_write;
2888 ma_proc pa_stream_write;
2889 ma_proc pa_stream_peek;
2890 ma_proc pa_stream_drop;
2891 ma_proc pa_stream_writable_size;
2892 ma_proc pa_stream_readable_size;
2893
2894 char* pApplicationName;
2895 char* pServerName;
2896 ma_bool32 tryAutoSpawn;
2897 } pulse;
2898#endif
2899#ifdef MA_SUPPORT_JACK
2900 struct
2901 {
2902 ma_handle jackSO;
2903 ma_proc jack_client_open;
2904 ma_proc jack_client_close;
2905 ma_proc jack_client_name_size;
2906 ma_proc jack_set_process_callback;
2907 ma_proc jack_set_buffer_size_callback;
2908 ma_proc jack_on_shutdown;
2909 ma_proc jack_get_sample_rate;
2910 ma_proc jack_get_buffer_size;
2911 ma_proc jack_get_ports;
2912 ma_proc jack_activate;
2913 ma_proc jack_deactivate;
2914 ma_proc jack_connect;
2915 ma_proc jack_port_register;
2916 ma_proc jack_port_name;
2917 ma_proc jack_port_get_buffer;
2918 ma_proc jack_free;
2919
2920 char* pClientName;
2921 ma_bool32 tryStartServer;
2922 } jack;
2923#endif
2924#ifdef MA_SUPPORT_COREAUDIO
2925 struct
2926 {
2927 ma_handle hCoreFoundation;
2928 ma_proc CFStringGetCString;
2929 ma_proc CFRelease;
2930
2931 ma_handle hCoreAudio;
2932 ma_proc AudioObjectGetPropertyData;
2933 ma_proc AudioObjectGetPropertyDataSize;
2934 ma_proc AudioObjectSetPropertyData;
2935 ma_proc AudioObjectAddPropertyListener;
2936 ma_proc AudioObjectRemovePropertyListener;
2937
2938 ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */
2939 ma_proc AudioComponentFindNext;
2940 ma_proc AudioComponentInstanceDispose;
2941 ma_proc AudioComponentInstanceNew;
2942 ma_proc AudioOutputUnitStart;
2943 ma_proc AudioOutputUnitStop;
2944 ma_proc AudioUnitAddPropertyListener;
2945 ma_proc AudioUnitGetPropertyInfo;
2946 ma_proc AudioUnitGetProperty;
2947 ma_proc AudioUnitSetProperty;
2948 ma_proc AudioUnitInitialize;
2949 ma_proc AudioUnitRender;
2950
2951 /*AudioComponent*/ ma_ptr component;
2952 } coreaudio;
2953#endif
2954#ifdef MA_SUPPORT_SNDIO
2955 struct
2956 {
2957 ma_handle sndioSO;
2958 ma_proc sio_open;
2959 ma_proc sio_close;
2960 ma_proc sio_setpar;
2961 ma_proc sio_getpar;
2962 ma_proc sio_getcap;
2963 ma_proc sio_start;
2964 ma_proc sio_stop;
2965 ma_proc sio_read;
2966 ma_proc sio_write;
2967 ma_proc sio_onmove;
2968 ma_proc sio_nfds;
2969 ma_proc sio_pollfd;
2970 ma_proc sio_revents;
2971 ma_proc sio_eof;
2972 ma_proc sio_setvol;
2973 ma_proc sio_onvol;
2974 ma_proc sio_initpar;
2975 } sndio;
2976#endif
2977#ifdef MA_SUPPORT_AUDIO4
2978 struct
2979 {
2980 int _unused;
2981 } audio4;
2982#endif
2983#ifdef MA_SUPPORT_OSS
2984 struct
2985 {
2986 int versionMajor;
2987 int versionMinor;
2988 } oss;
2989#endif
2990#ifdef MA_SUPPORT_AAUDIO
2991 struct
2992 {
2993 ma_handle hAAudio; /* libaaudio.so */
2994 ma_proc AAudio_createStreamBuilder;
2995 ma_proc AAudioStreamBuilder_delete;
2996 ma_proc AAudioStreamBuilder_setDeviceId;
2997 ma_proc AAudioStreamBuilder_setDirection;
2998 ma_proc AAudioStreamBuilder_setSharingMode;
2999 ma_proc AAudioStreamBuilder_setFormat;
3000 ma_proc AAudioStreamBuilder_setChannelCount;
3001 ma_proc AAudioStreamBuilder_setSampleRate;
3002 ma_proc AAudioStreamBuilder_setBufferCapacityInFrames;
3003 ma_proc AAudioStreamBuilder_setFramesPerDataCallback;
3004 ma_proc AAudioStreamBuilder_setDataCallback;
3005 ma_proc AAudioStreamBuilder_setErrorCallback;
3006 ma_proc AAudioStreamBuilder_setPerformanceMode;
3007 ma_proc AAudioStreamBuilder_openStream;
3008 ma_proc AAudioStream_close;
3009 ma_proc AAudioStream_getState;
3010 ma_proc AAudioStream_waitForStateChange;
3011 ma_proc AAudioStream_getFormat;
3012 ma_proc AAudioStream_getChannelCount;
3013 ma_proc AAudioStream_getSampleRate;
3014 ma_proc AAudioStream_getBufferCapacityInFrames;
3015 ma_proc AAudioStream_getFramesPerDataCallback;
3016 ma_proc AAudioStream_getFramesPerBurst;
3017 ma_proc AAudioStream_requestStart;
3018 ma_proc AAudioStream_requestStop;
3019 } aaudio;
3020#endif
3021#ifdef MA_SUPPORT_OPENSL
3022 struct
3023 {
3024 int _unused;
3025 } opensl;
3026#endif
3027#ifdef MA_SUPPORT_WEBAUDIO
3028 struct
3029 {
3030 int _unused;
3031 } webaudio;
3032#endif
3033#ifdef MA_SUPPORT_NULL
3034 struct
3035 {
3036 int _unused;
3037 } null_backend;
3038#endif
3039 };
3040
3041 union
3042 {
3043#ifdef MA_WIN32
3044 struct
3045 {
3046 /*HMODULE*/ ma_handle hOle32DLL;
3047 ma_proc CoInitializeEx;
3048 ma_proc CoUninitialize;
3049 ma_proc CoCreateInstance;
3050 ma_proc CoTaskMemFree;
3051 ma_proc PropVariantClear;
3052 ma_proc StringFromGUID2;
3053
3054 /*HMODULE*/ ma_handle hUser32DLL;
3055 ma_proc GetForegroundWindow;
3056 ma_proc GetDesktopWindow;
3057
3058 /*HMODULE*/ ma_handle hAdvapi32DLL;
3059 ma_proc RegOpenKeyExA;
3060 ma_proc RegCloseKey;
3061 ma_proc RegQueryValueExA;
3062 } win32;
3063#endif
3064#ifdef MA_POSIX
3065 struct
3066 {
3067 ma_handle pthreadSO;
3068 ma_proc pthread_create;
3069 ma_proc pthread_join;
3070 ma_proc pthread_mutex_init;
3071 ma_proc pthread_mutex_destroy;
3072 ma_proc pthread_mutex_lock;
3073 ma_proc pthread_mutex_unlock;
3074 ma_proc pthread_cond_init;
3075 ma_proc pthread_cond_destroy;
3076 ma_proc pthread_cond_wait;
3077 ma_proc pthread_cond_signal;
3078 ma_proc pthread_attr_init;
3079 ma_proc pthread_attr_destroy;
3080 ma_proc pthread_attr_setschedpolicy;
3081 ma_proc pthread_attr_getschedparam;
3082 ma_proc pthread_attr_setschedparam;
3083 } posix;
3084#endif
3085 int _unused;
3086 };
3087};
3088
3089struct ma_device
3090{
3091 ma_context* pContext;
3092 ma_device_type type;
3093 ma_uint32 sampleRate;
3094 volatile ma_uint32 state; /* The state of the device is variable and can change at any time on any thread, so tell the compiler as such with `volatile`. */
3095 ma_device_callback_proc onData; /* Set once at initialization time and should not be changed after. */
3096 ma_stop_proc onStop; /* Set once at initialization time and should not be changed after. */
3097 void* pUserData; /* Application defined data. */
3098 ma_mutex lock;
3099 ma_event wakeupEvent;
3100 ma_event startEvent;
3101 ma_event stopEvent;
3102 ma_thread thread;
3103 ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */
3104 ma_bool32 usingDefaultSampleRate : 1;
3105 ma_bool32 usingDefaultBufferSize : 1;
3106 ma_bool32 usingDefaultPeriods : 1;
3107 ma_bool32 isOwnerOfContext : 1; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */
3108 ma_bool32 noPreZeroedOutputBuffer : 1;
3109 ma_bool32 noClip : 1;
3110 volatile float masterVolumeFactor; /* Volatile so we can use some thread safety when applying volume to periods. */
3111 struct
3112 {
3113 ma_resample_algorithm algorithm;
3114 struct
3115 {
3116 ma_uint32 lpfCount;
3117 } linear;
3118 struct
3119 {
3120 int quality;
3121 } speex;
3122 } resampling;
3123 struct
3124 {
3125 char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */
3126 ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
3127 ma_bool32 usingDefaultFormat : 1;
3128 ma_bool32 usingDefaultChannels : 1;
3129 ma_bool32 usingDefaultChannelMap : 1;
3130 ma_format format;
3131 ma_uint32 channels;
3132 ma_channel channelMap[MA_MAX_CHANNELS];
3133 ma_format internalFormat;
3134 ma_uint32 internalChannels;
3135 ma_uint32 internalSampleRate;
3136 ma_channel internalChannelMap[MA_MAX_CHANNELS];
3137 ma_uint32 internalPeriodSizeInFrames;
3138 ma_uint32 internalPeriods;
3139 ma_data_converter converter;
3140 } playback;
3141 struct
3142 {
3143 char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */
3144 ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
3145 ma_bool32 usingDefaultFormat : 1;
3146 ma_bool32 usingDefaultChannels : 1;
3147 ma_bool32 usingDefaultChannelMap : 1;
3148 ma_format format;
3149 ma_uint32 channels;
3150 ma_channel channelMap[MA_MAX_CHANNELS];
3151 ma_format internalFormat;
3152 ma_uint32 internalChannels;
3153 ma_uint32 internalSampleRate;
3154 ma_channel internalChannelMap[MA_MAX_CHANNELS];
3155 ma_uint32 internalPeriodSizeInFrames;
3156 ma_uint32 internalPeriods;
3157 ma_data_converter converter;
3158 } capture;
3159
3160 union
3161 {
3162#ifdef MA_SUPPORT_WASAPI
3163 struct
3164 {
3165 /*IAudioClient**/ ma_ptr pAudioClientPlayback;
3166 /*IAudioClient**/ ma_ptr pAudioClientCapture;
3167 /*IAudioRenderClient**/ ma_ptr pRenderClient;
3168 /*IAudioCaptureClient**/ ma_ptr pCaptureClient;
3169 /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */
3170 ma_IMMNotificationClient notificationClient;
3171 /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */
3172 /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */
3173 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. */
3174 ma_uint32 actualPeriodSizeInFramesCapture;
3175 ma_uint32 originalPeriodSizeInFrames;
3176 ma_uint32 originalPeriodSizeInMilliseconds;
3177 ma_uint32 originalPeriods;
3178 ma_bool32 hasDefaultPlaybackDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
3179 ma_bool32 hasDefaultCaptureDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
3180 ma_uint32 periodSizeInFramesPlayback;
3181 ma_uint32 periodSizeInFramesCapture;
3182 ma_bool32 isStartedCapture; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
3183 ma_bool32 isStartedPlayback; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
3184 ma_bool32 noAutoConvertSRC : 1; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
3185 ma_bool32 noDefaultQualitySRC : 1; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
3186 ma_bool32 noHardwareOffloading : 1;
3187 ma_bool32 allowCaptureAutoStreamRouting : 1;
3188 ma_bool32 allowPlaybackAutoStreamRouting : 1;
3189 } wasapi;
3190#endif
3191#ifdef MA_SUPPORT_DSOUND
3192 struct
3193 {
3194 /*LPDIRECTSOUND*/ ma_ptr pPlayback;
3195 /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer;
3196 /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer;
3197 /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture;
3198 /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer;
3199 } dsound;
3200#endif
3201#ifdef MA_SUPPORT_WINMM
3202 struct
3203 {
3204 /*HWAVEOUT*/ ma_handle hDevicePlayback;
3205 /*HWAVEIN*/ ma_handle hDeviceCapture;
3206 /*HANDLE*/ ma_handle hEventPlayback;
3207 /*HANDLE*/ ma_handle hEventCapture;
3208 ma_uint32 fragmentSizeInFrames;
3209 ma_uint32 fragmentSizeInBytes;
3210 ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */
3211 ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */
3212 ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */
3213 ma_uint32 headerFramesConsumedCapture; /* ^^^ */
3214 /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */
3215 /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */
3216 ma_uint8* pIntermediaryBufferPlayback;
3217 ma_uint8* pIntermediaryBufferCapture;
3218 ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */
3219 } winmm;
3220#endif
3221#ifdef MA_SUPPORT_ALSA
3222 struct
3223 {
3224 /*snd_pcm_t**/ ma_ptr pPCMPlayback;
3225 /*snd_pcm_t**/ ma_ptr pPCMCapture;
3226 ma_bool32 isUsingMMapPlayback : 1;
3227 ma_bool32 isUsingMMapCapture : 1;
3228 } alsa;
3229#endif
3230#ifdef MA_SUPPORT_PULSEAUDIO
3231 struct
3232 {
3233 /*pa_mainloop**/ ma_ptr pMainLoop;
3234 /*pa_mainloop_api**/ ma_ptr pAPI;
3235 /*pa_context**/ ma_ptr pPulseContext;
3236 /*pa_stream**/ ma_ptr pStreamPlayback;
3237 /*pa_stream**/ ma_ptr pStreamCapture;
3238 /*pa_context_state*/ ma_uint32 pulseContextState;
3239 void* pMappedBufferPlayback;
3240 const void* pMappedBufferCapture;
3241 ma_uint32 mappedBufferFramesRemainingPlayback;
3242 ma_uint32 mappedBufferFramesRemainingCapture;
3243 ma_uint32 mappedBufferFramesCapacityPlayback;
3244 ma_uint32 mappedBufferFramesCapacityCapture;
3245 ma_bool32 breakFromMainLoop : 1;
3246 } pulse;
3247#endif
3248#ifdef MA_SUPPORT_JACK
3249 struct
3250 {
3251 /*jack_client_t**/ ma_ptr pClient;
3252 /*jack_port_t**/ ma_ptr pPortsPlayback[MA_MAX_CHANNELS];
3253 /*jack_port_t**/ ma_ptr pPortsCapture[MA_MAX_CHANNELS];
3254 float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */
3255 float* pIntermediaryBufferCapture;
3256 ma_pcm_rb duplexRB;
3257 } jack;
3258#endif
3259#ifdef MA_SUPPORT_COREAUDIO
3260 struct
3261 {
3262 ma_uint32 deviceObjectIDPlayback;
3263 ma_uint32 deviceObjectIDCapture;
3264 /*AudioUnit*/ ma_ptr audioUnitPlayback;
3265 /*AudioUnit*/ ma_ptr audioUnitCapture;
3266 /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */
3267 ma_event stopEvent;
3268 ma_uint32 originalPeriodSizeInFrames;
3269 ma_uint32 originalPeriodSizeInMilliseconds;
3270 ma_uint32 originalPeriods;
3271 ma_bool32 isDefaultPlaybackDevice;
3272 ma_bool32 isDefaultCaptureDevice;
3273 ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
3274 ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
3275 ma_pcm_rb duplexRB;
3276 void* pRouteChangeHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */
3277 } coreaudio;
3278#endif
3279#ifdef MA_SUPPORT_SNDIO
3280 struct
3281 {
3282 ma_ptr handlePlayback;
3283 ma_ptr handleCapture;
3284 ma_bool32 isStartedPlayback;
3285 ma_bool32 isStartedCapture;
3286 } sndio;
3287#endif
3288#ifdef MA_SUPPORT_AUDIO4
3289 struct
3290 {
3291 int fdPlayback;
3292 int fdCapture;
3293 } audio4;
3294#endif
3295#ifdef MA_SUPPORT_OSS
3296 struct
3297 {
3298 int fdPlayback;
3299 int fdCapture;
3300 } oss;
3301#endif
3302#ifdef MA_SUPPORT_AAUDIO
3303 struct
3304 {
3305 /*AAudioStream**/ ma_ptr pStreamPlayback;
3306 /*AAudioStream**/ ma_ptr pStreamCapture;
3307 ma_pcm_rb duplexRB;
3308 } aaudio;
3309#endif
3310#ifdef MA_SUPPORT_OPENSL
3311 struct
3312 {
3313 /*SLObjectItf*/ ma_ptr pOutputMixObj;
3314 /*SLOutputMixItf*/ ma_ptr pOutputMix;
3315 /*SLObjectItf*/ ma_ptr pAudioPlayerObj;
3316 /*SLPlayItf*/ ma_ptr pAudioPlayer;
3317 /*SLObjectItf*/ ma_ptr pAudioRecorderObj;
3318 /*SLRecordItf*/ ma_ptr pAudioRecorder;
3319 /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback;
3320 /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture;
3321 ma_bool32 isDrainingCapture;
3322 ma_bool32 isDrainingPlayback;
3323 ma_uint32 currentBufferIndexPlayback;
3324 ma_uint32 currentBufferIndexCapture;
3325 ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */
3326 ma_uint8* pBufferCapture;
3327 ma_pcm_rb duplexRB;
3328 } opensl;
3329#endif
3330#ifdef MA_SUPPORT_WEBAUDIO
3331 struct
3332 {
3333 int indexPlayback; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */
3334 int indexCapture;
3335 ma_pcm_rb duplexRB; /* In external capture format. */
3336 } webaudio;
3337#endif
3338#ifdef MA_SUPPORT_NULL
3339 struct
3340 {
3341 ma_thread deviceThread;
3342 ma_event operationEvent;
3343 ma_event operationCompletionEvent;
3344 ma_uint32 operation;
3345 ma_result operationResult;
3346 ma_timer timer;
3347 double priorRunTime;
3348 ma_uint32 currentPeriodFramesRemainingPlayback;
3349 ma_uint32 currentPeriodFramesRemainingCapture;
3350 ma_uint64 lastProcessedFramePlayback;
3351 ma_uint32 lastProcessedFrameCapture;
3352 ma_bool32 isStarted;
3353 } null_device;
3354#endif
3355 };
3356};
3357#if defined(_MSC_VER) && !defined(__clang__)
3358 #pragma warning(pop)
3359#else
3360 #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
3361#endif
3362
3363/*
3364Initializes a `ma_context_config` object.
3365
3366
3367Return Value
3368------------
3369A `ma_context_config` initialized to defaults.
3370
3371
3372Remarks
3373-------
3374You 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
3375is updated and new members are added to `ma_context_config`. It also sets logical defaults.
3376
3377You can override members of the returned object by changing it's members directly.
3378
3379
3380See Also
3381--------
3382ma_context_init()
3383*/
3384ma_context_config ma_context_config_init(void);
3385
3386/*
3387Initializes a context.
3388
3389The 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
3390device. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices.
3391
3392
3393Parameters
3394----------
3395backends (in, optional)
3396 A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order.
3397
3398backendCount (in, optional)
3399 The number of items in `backend`. Ignored if `backend` is NULL.
3400
3401pConfig (in, optional)
3402 The context configuration.
3403
3404pContext (in)
3405 A pointer to the context object being initialized.
3406
3407
3408Return Value
3409------------
3410MA_SUCCESS if successful; any other error code otherwise.
3411
3412
3413Thread Safety
3414-------------
3415Unsafe. Do not call this function across multiple threads as some backends read and write to global state.
3416
3417
3418Remarks
3419-------
3420When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order:
3421
3422 |-------------|-----------------------|--------------------------------------------------------|
3423 | Name | Enum Name | Supported Operating Systems |
3424 |-------------|-----------------------|--------------------------------------------------------|
3425 | WASAPI | ma_backend_wasapi | Windows Vista+ |
3426 | DirectSound | ma_backend_dsound | Windows XP+ |
3427 | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) |
3428 | Core Audio | ma_backend_coreaudio | macOS, iOS |
3429 | ALSA | ma_backend_alsa | Linux |
3430 | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
3431 | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
3432 | sndio | ma_backend_sndio | OpenBSD |
3433 | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
3434 | OSS | ma_backend_oss | FreeBSD |
3435 | AAudio | ma_backend_aaudio | Android 8+ |
3436 | OpenSL|ES | ma_backend_opensl | Android (API level 16+) |
3437 | Web Audio | ma_backend_webaudio | Web (via Emscripten) |
3438 | Null | ma_backend_null | Cross Platform (not used on Web) |
3439 |-------------|-----------------------|--------------------------------------------------------|
3440
3441The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings
3442can then be set directly on the structure. Below are the members of the `ma_context_config` object.
3443
3444 logCallback
3445 Callback for handling log messages from miniaudio.
3446
3447 threadPriority
3448 The desired priority to use for the audio thread. Allowable values include the following:
3449
3450 |--------------------------------------|
3451 | Thread Priority |
3452 |--------------------------------------|
3453 | ma_thread_priority_idle |
3454 | ma_thread_priority_lowest |
3455 | ma_thread_priority_low |
3456 | ma_thread_priority_normal |
3457 | ma_thread_priority_high |
3458 | ma_thread_priority_highest (default) |
3459 | ma_thread_priority_realtime |
3460 | ma_thread_priority_default |
3461 |--------------------------------------|
3462
3463 pUserData
3464 A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`.
3465
3466 allocationCallbacks
3467 Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation
3468 callbacks will be used for anything tied to the context, including devices.
3469
3470 alsa.useVerboseDeviceEnumeration
3471 ALSA will typically enumerate many different devices which can be intrusive and unuser-friendly. To combat this, miniaudio will enumerate only unique
3472 card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes
3473 it so the ALSA backend includes all devices. Defaults to false.
3474
3475 pulse.pApplicationName
3476 PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`.
3477
3478 pulse.pServerName
3479 PulseAudio only. The name of the server to connect to with `pa_context_connect()`.
3480
3481 pulse.tryAutoSpawn
3482 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
3483 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
3484 intrusive for the end user.
3485
3486 coreaudio.sessionCategory
3487 iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.
3488
3489 |-----------------------------------------|-------------------------------------|
3490 | miniaudio Token | Core Audio Token |
3491 |-----------------------------------------|-------------------------------------|
3492 | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient |
3493 | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient |
3494 | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback |
3495 | ma_ios_session_category_record | AVAudioSessionCategoryRecord |
3496 | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord |
3497 | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute |
3498 | ma_ios_session_category_none | AVAudioSessionCategoryAmbient |
3499 | ma_ios_session_category_default | AVAudioSessionCategoryAmbient |
3500 |-----------------------------------------|-------------------------------------|
3501
3502 coreaudio.sessionCategoryOptions
3503 iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.
3504
3505 |---------------------------------------------------------------------------|------------------------------------------------------------------|
3506 | miniaudio Token | Core Audio Token |
3507 |---------------------------------------------------------------------------|------------------------------------------------------------------|
3508 | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers |
3509 | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers |
3510 | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth |
3511 | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker |
3512 | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers |
3513 | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP |
3514 | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay |
3515 |---------------------------------------------------------------------------|------------------------------------------------------------------|
3516
3517 jack.pClientName
3518 The name of the client to pass to `jack_client_open()`.
3519
3520 jack.tryStartServer
3521 Whether or not to try auto-starting the JACK server. Defaults to false.
3522
3523
3524It 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
3525relevant backends every time it's initialized.
3526
3527The location of the context cannot change throughout it's lifetime. Consider allocating the `ma_context` object with `malloc()` if this is an issue. The
3528reason for this is that a pointer to the context is stored in the `ma_device` structure.
3529
3530
3531Example 1 - Default Initialization
3532----------------------------------
3533The example below shows how to initialize the context using the default configuration.
3534
3535```c
3536ma_context context;
3537ma_result result = ma_context_init(NULL, 0, NULL, &context);
3538if (result != MA_SUCCESS) {
3539 // Error.
3540}
3541```
3542
3543
3544Example 2 - Custom Configuration
3545--------------------------------
3546The example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program
3547wants 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
3548want an error to be returned if no valid backend is available which they achieve by excluding the Null backend.
3549
3550For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface.
3551
3552```c
3553ma_backend backends[] = {
3554 ma_backend_alsa,
3555 ma_backend_pulseaudio,
3556 ma_backend_wasapi,
3557 ma_backend_dsound
3558};
3559
3560ma_context_config config = ma_context_config_init();
3561config.logCallback = my_log_callback;
3562config.pUserData = pMyUserData;
3563
3564ma_context context;
3565ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context);
3566if (result != MA_SUCCESS) {
3567 // Error.
3568 if (result == MA_NO_BACKEND) {
3569 // Couldn't find an appropriate backend.
3570 }
3571}
3572```
3573
3574
3575See Also
3576--------
3577ma_context_config_init()
3578ma_context_uninit()
3579*/
3580ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext);
3581
3582/*
3583Uninitializes a context.
3584
3585
3586Return Value
3587------------
3588MA_SUCCESS if successful; any other error code otherwise.
3589
3590
3591Thread Safety
3592-------------
3593Unsafe. Do not call this function across multiple threads as some backends read and write to global state.
3594
3595
3596Remarks
3597-------
3598Results are undefined if you call this while any device created by this context is still active.
3599
3600
3601See Also
3602--------
3603ma_context_init()
3604*/
3605ma_result ma_context_uninit(ma_context* pContext);
3606
3607/*
3608Enumerates over every device (both playback and capture).
3609
3610This 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
3611an internal heap allocation, or it simply suits your code better.
3612
3613Note 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
3614opening 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,
3615but don't call it from within the enumeration callback.
3616
3617Returning false from the callback will stop enumeration. Returning true will continue enumeration.
3618
3619
3620Parameters
3621----------
3622pContext (in)
3623 A pointer to the context performing the enumeration.
3624
3625callback (in)
3626 The callback to fire for each enumerated device.
3627
3628pUserData (in)
3629 A pointer to application-defined data passed to the callback.
3630
3631
3632Return Value
3633------------
3634MA_SUCCESS if successful; any other error code otherwise.
3635
3636
3637Thread Safety
3638-------------
3639Safe. This is guarded using a simple mutex lock.
3640
3641
3642Remarks
3643-------
3644Do _not_ assume the first enumerated device of a given type is the default device.
3645
3646Some backends and platforms may only support default playback and capture devices.
3647
3648In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also,
3649do not try to call `ma_context_get_device_info()` from within the callback.
3650
3651Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation.
3652
3653
3654Example 1 - Simple Enumeration
3655------------------------------
3656ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
3657{
3658 printf("Device Name: %s\n", pInfo->name);
3659 return MA_TRUE;
3660}
3661
3662ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData);
3663if (result != MA_SUCCESS) {
3664 // Error.
3665}
3666
3667
3668See Also
3669--------
3670ma_context_get_devices()
3671*/
3672ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData);
3673
3674/*
3675Retrieves basic information about every active playback and/or capture device.
3676
3677This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos`
3678parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback.
3679
3680
3681Parameters
3682----------
3683pContext (in)
3684 A pointer to the context performing the enumeration.
3685
3686ppPlaybackDeviceInfos (out)
3687 A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices.
3688
3689pPlaybackDeviceCount (out)
3690 A pointer to an unsigned integer that will receive the number of playback devices.
3691
3692ppCaptureDeviceInfos (out)
3693 A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices.
3694
3695pCaptureDeviceCount (out)
3696 A pointer to an unsigned integer that will receive the number of capture devices.
3697
3698
3699Return Value
3700------------
3701MA_SUCCESS if successful; any other error code otherwise.
3702
3703
3704Thread Safety
3705-------------
3706Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple
3707threads. Instead, you need to make a copy of the returned data with your own higher level synchronization.
3708
3709
3710Remarks
3711-------
3712It is _not_ safe to assume the first device in the list is the default device.
3713
3714You can pass in NULL for the playback or capture lists in which case they'll be ignored.
3715
3716The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers.
3717
3718
3719See Also
3720--------
3721ma_context_get_devices()
3722*/
3723ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount);
3724
3725/*
3726Retrieves information about a device of the given type, with the specified ID and share mode.
3727
3728
3729Parameters
3730----------
3731pContext (in)
3732 A pointer to the context performing the query.
3733
3734deviceType (in)
3735 The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`.
3736
3737pDeviceID (in)
3738 The ID of the device being queried.
3739
3740shareMode (in)
3741 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,
3742 set this to `ma_share_mode_shared`.
3743
3744pDeviceInfo (out)
3745 A pointer to the `ma_device_info` structure that will receive the device information.
3746
3747
3748Return Value
3749------------
3750MA_SUCCESS if successful; any other error code otherwise.
3751
3752
3753Thread Safety
3754-------------
3755Safe. This is guarded using a simple mutex lock.
3756
3757
3758Remarks
3759-------
3760Do _not_ call this from within the `ma_context_enumerate_devices()` callback.
3761
3762It'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
3763shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify
3764which 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
3765the requested share mode is unsupported.
3766
3767This leaves pDeviceInfo unmodified in the result of an error.
3768*/
3769ma_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);
3770
3771/*
3772Determines if the given context supports loopback mode.
3773
3774
3775Parameters
3776----------
3777pContext (in)
3778 A pointer to the context getting queried.
3779
3780
3781Return Value
3782------------
3783MA_TRUE if the context supports loopback mode; MA_FALSE otherwise.
3784*/
3785ma_bool32 ma_context_is_loopback_supported(ma_context* pContext);
3786
3787
3788
3789/*
3790Initializes a device config with default settings.
3791
3792
3793Parameters
3794----------
3795deviceType (in)
3796 The type of the device this config is being initialized for. This must set to one of the following:
3797
3798 |-------------------------|
3799 | Device Type |
3800 |-------------------------|
3801 | ma_device_type_playback |
3802 | ma_device_type_capture |
3803 | ma_device_type_duplex |
3804 | ma_device_type_loopback |
3805 |-------------------------|
3806
3807
3808Return Value
3809------------
3810A new device config object with default settings. You will typically want to adjust the config after this function returns. See remarks.
3811
3812
3813Thread Safety
3814-------------
3815Safe.
3816
3817
3818Callback Safety
3819---------------
3820Safe, but don't try initializing a device in a callback.
3821
3822
3823Remarks
3824-------
3825The 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
3826typical configuration which sets the sample format, channel count, sample rate, data callback and user data. These are usually things you will want to change
3827before initializing the device.
3828
3829See `ma_device_init()` for details on specific configuration options.
3830
3831
3832Example 1 - Simple Configuration
3833--------------------------------
3834The 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
3835then the returned object is modified directly. This is important because it ensures that your program continues to work as new configuration options are added
3836to the `ma_device_config` structure.
3837
3838```c
3839ma_device_config config = ma_device_config_init(ma_device_type_playback);
3840config.playback.format = ma_format_f32;
3841config.playback.channels = 2;
3842config.sampleRate = 48000;
3843config.dataCallback = ma_data_callback;
3844config.pUserData = pMyUserData;
3845```
3846
3847
3848See Also
3849--------
3850ma_device_init()
3851ma_device_init_ex()
3852*/
3853ma_device_config ma_device_config_init(ma_device_type deviceType);
3854
3855
3856/*
3857Initializes a device.
3858
3859A 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
3860from 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
3861playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the
3862device is done via a callback which is fired by miniaudio at periodic time intervals.
3863
3864The frequency at which data is deilvered to and from a device depends on the size of it's period which is defined by a buffer size and a period count. The size
3865of the buffer can be defined in terms of PCM frames or milliseconds, whichever is more convenient. The size of a period is the size of this buffer, divided by
3866the period count. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and increased risk of glitching due to
3867the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but miniaudio's defaults should work fine for
3868most scenarios. If you're building a game you should leave this fairly small, whereas if you're building a simple media player you can make it larger. Note
3869that the period size you request is actually just a hint - miniaudio will tell the backend what you want, but the backend is ultimately responsible for what it
3870gives you. You cannot assume you will get exactly what you ask for.
3871
3872When 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
3873format 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
3874can 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.
3875
3876
3877Parameters
3878----------
3879pContext (in, optional)
3880 A pointer to the context that owns the device. This can be null, in which case it creates a default context internally.
3881
3882pConfig (in)
3883 A pointer to the device configuration. Cannot be null. See remarks for details.
3884
3885pDevice (out)
3886 A pointer to the device object being initialized.
3887
3888
3889Return Value
3890------------
3891MA_SUCCESS if successful; any other error code otherwise.
3892
3893
3894Thread Safety
3895-------------
3896Unsafe. 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
3897calling this at the same time as `ma_device_uninit()`.
3898
3899
3900Callback Safety
3901---------------
3902Unsafe. It is not safe to call this inside any callback.
3903
3904
3905Remarks
3906-------
3907Setting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so:
3908
3909 ```c
3910 ma_context_init(NULL, 0, NULL, &context);
3911 ```
3912
3913Do 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
3914device.pContext for the initialization of other devices.
3915
3916The device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can
3917then be set directly on the structure. Below are the members of the `ma_device_config` object.
3918
3919 deviceType
3920 Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`.
3921
3922 sampleRate
3923 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.
3924
3925 periodSizeInFrames
3926 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
3927 be used depending on the selected performance profile. This value affects latency. See below for details.
3928
3929 periodSizeInMilliseconds
3930 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
3931 used depending on the selected performance profile. The value affects latency. See below for details.
3932
3933 periods
3934 The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by
3935 this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured.
3936
3937 performanceProfile
3938 A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or
3939 `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value.
3940
3941 noPreZeroedOutputBuffer
3942 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
3943 the output buffer will be cleared the zero. You can use this to avoid the overhead of zeroing out the buffer if you know can guarantee that your data
3944 callback will write to every sample in the output buffer, or if you are doing your own clearing.
3945
3946 noClip
3947 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
3948 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
3949 applies when the playback sample format is f32.
3950
3951 dataCallback
3952 The callback to fire whenever data is ready to be delivered to or from the device.
3953
3954 stopCallback
3955 The callback to fire whenever the device has stopped, either explicitly via `ma_device_stop()`, or implicitly due to things like the device being
3956 disconnected.
3957
3958 pUserData
3959 The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`.
3960
3961 resampling.algorithm
3962 The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The
3963 default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfCount`.
3964
3965 resampling.linear.lpfCount
3966 The linear resampler applies a low-pass filter as part of it's procesing for anti-aliasing. This setting controls the quality of the filter. The higher
3967 the value, the better the quality. Setting this to 0 will disable low-pass filtering altogether. The maximum value is `MA_MAX_RESAMPLER_LPF_FILTERS`.
3968 The default value is `min(2, MA_MAX_RESAMPLER_LPF_FILTERS)`.
3969
3970 playback.pDeviceID
3971 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
3972 default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration.
3973
3974 playback.format
3975 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
3976 initialization from the device object directly with `device.playback.format`.
3977
3978 playback.channels
3979 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
3980 from the device object directly with `device.playback.channels`.
3981
3982 playback.channelMap
3983 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
3984 device object direct with `device.playback.channelMap`.
3985
3986 playback.shareMode
3987 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
3988 exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired.
3989
3990 playback.pDeviceID
3991 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
3992 default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration.
3993
3994 capture.format
3995 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
3996 initialization from the device object directly with `device.capture.format`.
3997
3998 capture.channels
3999 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
4000 from the device object directly with `device.capture.channels`.
4001
4002 capture.channelMap
4003 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
4004 device object direct with `device.capture.channelMap`.
4005
4006 capture.shareMode
4007 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
4008 exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired.
4009
4010 wasapi.noAutoConvertSRC
4011 WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false.
4012
4013 wasapi.noDefaultQualitySRC
4014 WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`.
4015 You should usually leave this set to false, which is the default.
4016
4017 wasapi.noAutoStreamRouting
4018 WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false.
4019
4020 wasapi.noHardwareOffloading
4021 WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false.
4022
4023 alsa.noMMap
4024 ALSA only. When set to true, disables MMap mode. Defaults to false.
4025
4026 pulse.pStreamNamePlayback
4027 PulseAudio only. Sets the stream name for playback.
4028
4029 pulse.pStreamNameCapture
4030 PulseAudio only. Sets the stream name for capture.
4031
4032
4033Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device.
4034
4035If both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or
4036`MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or
4037`ma_performance_profile_conservative`.
4038
4039If 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
4040in 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
4041config) 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,
4042for 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.
4043Starting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary.
4044
4045After initializing the device it will be in a stopped state. To start it, use `ma_device_start()`.
4046
4047When sending or receiving data to/from a device, miniaudio will internally perform a format conversion to convert between the format specified by pConfig and
4048the 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 on
4049an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`,
4050`playback/capture.channels` and `sampleRate` members of the device object.
4051
4052When 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
4053asking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information.
4054
4055ALSA Specific: When initializing the default device, requesting shared mode will try using the "dmix" device for playback and the "dsnoop" device for capture.
4056If these fail it will try falling back to the "hw" device.
4057
4058
4059Example 1 - Simple Initialization
4060---------------------------------
4061This example shows how to initialize a simple playback default using a standard configuration. If you are just needing to do simple playback from the default
4062playback device this is usually all you need.
4063
4064```c
4065ma_device_config config = ma_device_config_init(ma_device_type_playback);
4066config.playback.format = ma_format_f32;
4067config.playback.channels = 2;
4068config.sampleRate = 48000;
4069config.dataCallback = ma_data_callback;
4070config.pMyUserData = pMyUserData;
4071
4072ma_device device;
4073ma_result result = ma_device_init(NULL, &config, &device);
4074if (result != MA_SUCCESS) {
4075 // Error
4076}
4077```
4078
4079
4080Example 2 - Advanced Initialization
4081-----------------------------------
4082This example show how you might do some more advanced initialization. In this hypothetical example we want to control the latency by setting the buffer size
4083and 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
4084enumeration.
4085
4086```c
4087ma_context context;
4088ma_result result = ma_context_init(NULL, 0, NULL, &context);
4089if (result != MA_SUCCESS) {
4090 // Error
4091}
4092
4093ma_device_info* pPlaybackDeviceInfos;
4094ma_uint32 playbackDeviceCount;
4095result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL);
4096if (result != MA_SUCCESS) {
4097 // Error
4098}
4099
4100// ... choose a device from pPlaybackDeviceInfos ...
4101
4102ma_device_config config = ma_device_config_init(ma_device_type_playback);
4103config.playback.pDeviceID = pMyChosenDeviceID; // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices().
4104config.playback.format = ma_format_f32;
4105config.playback.channels = 2;
4106config.sampleRate = 48000;
4107config.dataCallback = ma_data_callback;
4108config.pUserData = pMyUserData;
4109config.periodSizeInMilliseconds = 10;
4110config.periods = 3;
4111
4112ma_device device;
4113result = ma_device_init(&context, &config, &device);
4114if (result != MA_SUCCESS) {
4115 // Error
4116}
4117```
4118
4119
4120See Also
4121--------
4122ma_device_config_init()
4123ma_device_uninit()
4124ma_device_start()
4125ma_context_init()
4126ma_context_get_devices()
4127ma_context_enumerate_devices()
4128*/
4129ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice);
4130
4131/*
4132Initializes a device without a context, with extra parameters for controlling the configuration of the internal self-managed context.
4133
4134This 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
4135allows you to configure the internally created context.
4136
4137
4138Parameters
4139----------
4140backends (in, optional)
4141 A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order.
4142
4143backendCount (in, optional)
4144 The number of items in `backend`. Ignored if `backend` is NULL.
4145
4146pContextConfig (in, optional)
4147 The context configuration.
4148
4149pConfig (in)
4150 A pointer to the device configuration. Cannot be null. See remarks for details.
4151
4152pDevice (out)
4153 A pointer to the device object being initialized.
4154
4155
4156Return Value
4157------------
4158MA_SUCCESS if successful; any other error code otherwise.
4159
4160
4161Thread Safety
4162-------------
4163Unsafe. 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
4164calling this at the same time as `ma_device_uninit()`.
4165
4166
4167Callback Safety
4168---------------
4169Unsafe. It is not safe to call this inside any callback.
4170
4171
4172Remarks
4173-------
4174You 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
4175your own context.
4176
4177See the documentation for `ma_context_init()` for information on the different context configuration options.
4178
4179
4180See Also
4181--------
4182ma_device_init()
4183ma_device_uninit()
4184ma_device_config_init()
4185ma_context_init()
4186*/
4187ma_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);
4188
4189/*
4190Uninitializes a device.
4191
4192This will explicitly stop the device. You do not need to call `ma_device_stop()` beforehand, but it's harmless if you do.
4193
4194
4195Parameters
4196----------
4197pDevice (in)
4198 A pointer to the device to stop.
4199
4200
4201Return Value
4202------------
4203MA_SUCCESS if successful; any other error code otherwise.
4204
4205
4206Thread Safety
4207-------------
4208Unsafe. As soon as this API is called the device should be considered undefined.
4209
4210
4211Callback Safety
4212---------------
4213Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.
4214
4215
4216See Also
4217--------
4218ma_device_init()
4219ma_device_stop()
4220*/
4221void ma_device_uninit(ma_device* pDevice);
4222
4223/*
4224Starts the device. For playback devices this begins playback. For capture devices it begins recording.
4225
4226Use `ma_device_stop()` to stop the device.
4227
4228
4229Parameters
4230----------
4231pDevice (in)
4232 A pointer to the device to start.
4233
4234
4235Return Value
4236------------
4237MA_SUCCESS if successful; any other error code otherwise.
4238
4239
4240Thread Safety
4241-------------
4242Safe. It's safe to call this from any thread with the exception of the callback thread.
4243
4244
4245Callback Safety
4246---------------
4247Unsafe. It is not safe to call this inside any callback.
4248
4249
4250Remarks
4251-------
4252For 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
4253audio data in the buffer, which needs to be done before the device begins playback.
4254
4255This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety.
4256
4257Do not call this in any callback.
4258
4259
4260See Also
4261--------
4262ma_device_stop()
4263*/
4264ma_result ma_device_start(ma_device* pDevice);
4265
4266/*
4267Stops the device. For playback devices this stops playback. For capture devices it stops recording.
4268
4269Use `ma_device_start()` to start the device again.
4270
4271
4272Parameters
4273----------
4274pDevice (in)
4275 A pointer to the device to stop.
4276
4277
4278Return Value
4279------------
4280MA_SUCCESS if successful; any other error code otherwise.
4281
4282
4283Thread Safety
4284-------------
4285Safe. It's safe to call this from any thread with the exception of the callback thread.
4286
4287
4288Callback Safety
4289---------------
4290Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.
4291
4292
4293Remarks
4294-------
4295This 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
4296backends 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
4297that was specified at initialization time).
4298
4299Backends 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
4300the 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
4301speakers or received from the microphone which can in turn result in de-syncs.
4302
4303Do not call this in any callback.
4304
4305This will be called implicitly by `ma_device_uninit()`.
4306
4307
4308See Also
4309--------
4310ma_device_start()
4311*/
4312ma_result ma_device_stop(ma_device* pDevice);
4313
4314/*
4315Determines whether or not the device is started.
4316
4317
4318Parameters
4319----------
4320pDevice (in)
4321 A pointer to the device whose start state is being retrieved.
4322
4323
4324Return Value
4325------------
4326True if the device is started, false otherwise.
4327
4328
4329Thread Safety
4330-------------
4331Safe. 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
4332value will be out of sync.
4333
4334
4335Callback Safety
4336---------------
4337Safe. This is implemented as a simple accessor.
4338
4339
4340See Also
4341--------
4342ma_device_start()
4343ma_device_stop()
4344*/
4345ma_bool32 ma_device_is_started(ma_device* pDevice);
4346
4347/*
4348Sets the master volume factor for the device.
4349
4350The 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
4351values less than 0 decreases the volume.
4352
4353
4354Parameters
4355----------
4356pDevice (in)
4357 A pointer to the device whose volume is being set.
4358
4359volume (in)
4360 The new volume factor. Must be within the range of [0, 1].
4361
4362
4363Return Value
4364------------
4365MA_SUCCESS if the volume was set successfully.
4366MA_INVALID_ARGS if pDevice is NULL.
4367MA_INVALID_ARGS if the volume factor is not within the range of [0, 1].
4368
4369
4370Thread Safety
4371-------------
4372Safe. This just sets a local member of the device object.
4373
4374
4375Callback Safety
4376---------------
4377Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied.
4378
4379
4380Remarks
4381-------
4382This applies the volume factor across all channels.
4383
4384This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream.
4385
4386
4387See Also
4388--------
4389ma_device_get_master_volume()
4390ma_device_set_master_volume_gain_db()
4391ma_device_get_master_volume_gain_db()
4392*/
4393ma_result ma_device_set_master_volume(ma_device* pDevice, float volume);
4394
4395/*
4396Retrieves the master volume factor for the device.
4397
4398
4399Parameters
4400----------
4401pDevice (in)
4402 A pointer to the device whose volume factor is being retrieved.
4403
4404pVolume (in)
4405 A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1].
4406
4407
4408Return Value
4409------------
4410MA_SUCCESS if successful.
4411MA_INVALID_ARGS if pDevice is NULL.
4412MA_INVALID_ARGS if pVolume is NULL.
4413
4414
4415Thread Safety
4416-------------
4417Safe. This just a simple member retrieval.
4418
4419
4420Callback Safety
4421---------------
4422Safe.
4423
4424
4425Remarks
4426-------
4427If an error occurs, `*pVolume` will be set to 0.
4428
4429
4430See Also
4431--------
4432ma_device_set_master_volume()
4433ma_device_set_master_volume_gain_db()
4434ma_device_get_master_volume_gain_db()
4435*/
4436ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume);
4437
4438/*
4439Sets the master volume for the device as gain in decibels.
4440
4441A gain of 0 is full volume, whereas a gain of < 0 will decrease the volume.
4442
4443
4444Parameters
4445----------
4446pDevice (in)
4447 A pointer to the device whose gain is being set.
4448
4449gainDB (in)
4450 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.
4451
4452
4453Return Value
4454------------
4455MA_SUCCESS if the volume was set successfully.
4456MA_INVALID_ARGS if pDevice is NULL.
4457MA_INVALID_ARGS if the gain is > 0.
4458
4459
4460Thread Safety
4461-------------
4462Safe. This just sets a local member of the device object.
4463
4464
4465Callback Safety
4466---------------
4467Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied.
4468
4469
4470Remarks
4471-------
4472This applies the gain across all channels.
4473
4474This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream.
4475
4476
4477See Also
4478--------
4479ma_device_get_master_volume_gain_db()
4480ma_device_set_master_volume()
4481ma_device_get_master_volume()
4482*/
4483ma_result ma_device_set_master_gain_db(ma_device* pDevice, float gainDB);
4484
4485/*
4486Retrieves the master gain in decibels.
4487
4488
4489Parameters
4490----------
4491pDevice (in)
4492 A pointer to the device whose gain is being retrieved.
4493
4494pGainDB (in)
4495 A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0.
4496
4497
4498Return Value
4499------------
4500MA_SUCCESS if successful.
4501MA_INVALID_ARGS if pDevice is NULL.
4502MA_INVALID_ARGS if pGainDB is NULL.
4503
4504
4505Thread Safety
4506-------------
4507Safe. This just a simple member retrieval.
4508
4509
4510Callback Safety
4511---------------
4512Safe.
4513
4514
4515Remarks
4516-------
4517If an error occurs, `*pGainDB` will be set to 0.
4518
4519
4520See Also
4521--------
4522ma_device_set_master_volume_gain_db()
4523ma_device_set_master_volume()
4524ma_device_get_master_volume()
4525*/
4526ma_result ma_device_get_master_gain_db(ma_device* pDevice, float* pGainDB);
4527
4528
4529
4530/************************************************************************************************************************************************************
4531
4532Utiltities
4533
4534************************************************************************************************************************************************************/
4535
4536/*
4537Creates a mutex.
4538
4539A mutex must be created from a valid context. A mutex is initially unlocked.
4540*/
4541ma_result ma_mutex_init(ma_context* pContext, ma_mutex* pMutex);
4542
4543/*
4544Deletes a mutex.
4545*/
4546void ma_mutex_uninit(ma_mutex* pMutex);
4547
4548/*
4549Locks a mutex with an infinite timeout.
4550*/
4551void ma_mutex_lock(ma_mutex* pMutex);
4552
4553/*
4554Unlocks a mutex.
4555*/
4556void ma_mutex_unlock(ma_mutex* pMutex);
4557
4558
4559/*
4560Retrieves a friendly name for a backend.
4561*/
4562const char* ma_get_backend_name(ma_backend backend);
4563
4564/*
4565Determines whether or not loopback mode is support by a backend.
4566*/
4567ma_bool32 ma_is_loopback_supported(ma_backend backend);
4568
4569
4570/*
4571Adjust buffer size based on a scaling factor.
4572
4573This just multiplies the base size by the scaling factor, making sure it's a size of at least 1.
4574*/
4575ma_uint32 ma_scale_buffer_size(ma_uint32 baseBufferSize, float scale);
4576
4577/*
4578Calculates a buffer size in milliseconds from the specified number of frames and sample rate.
4579*/
4580ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate);
4581
4582/*
4583Calculates a buffer size in frames from the specified number of milliseconds and sample rate.
4584*/
4585ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate);
4586
4587/*
4588Copies silent frames into the given buffer.
4589*/
4590void ma_zero_pcm_frames(void* p, ma_uint32 frameCount, ma_format format, ma_uint32 channels);
4591
4592/*
4593Clips f32 samples.
4594*/
4595void ma_clip_samples_f32(float* p, ma_uint32 sampleCount);
4596MA_INLINE void ma_clip_pcm_frames_f32(float* p, ma_uint32 frameCount, ma_uint32 channels) { ma_clip_samples_f32(p, frameCount*channels); }
4597
4598/*
4599Helper for applying a volume factor to samples.
4600
4601Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place.
4602*/
4603void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint32 sampleCount, float factor);
4604void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint32 sampleCount, float factor);
4605void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint32 sampleCount, float factor);
4606void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint32 sampleCount, float factor);
4607void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint32 sampleCount, float factor);
4608
4609void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint32 sampleCount, float factor);
4610void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint32 sampleCount, float factor);
4611void ma_apply_volume_factor_s24(void* pSamples, ma_uint32 sampleCount, float factor);
4612void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint32 sampleCount, float factor);
4613void ma_apply_volume_factor_f32(float* pSamples, ma_uint32 sampleCount, float factor);
4614
4615void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
4616void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
4617void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
4618void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
4619void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
4620void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor);
4621
4622void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
4623void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
4624void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
4625void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
4626void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
4627void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor);
4628
4629
4630/*
4631Helper for converting a linear factor to gain in decibels.
4632*/
4633float ma_factor_to_gain_db(float factor);
4634
4635/*
4636Helper for converting gain in decibels to a linear factor.
4637*/
4638float ma_gain_db_to_factor(float gain);
4639
4640#endif /* MA_NO_DEVICE_IO */
4641
4642
4643
4644
4645/************************************************************************************************************************************************************
4646
4647Decoding
4648========
4649
4650Decoders 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
4651you do your own synchronization.
4652
4653************************************************************************************************************************************************************/
4654#ifndef MA_NO_DECODING
4655
4656typedef struct ma_decoder ma_decoder;
4657
4658typedef enum
4659{
4660 ma_seek_origin_start,
4661 ma_seek_origin_current
4662} ma_seek_origin;
4663
4664typedef size_t (* ma_decoder_read_proc) (ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead); /* Returns the number of bytes read. */
4665typedef ma_bool32 (* ma_decoder_seek_proc) (ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin);
4666typedef 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. */
4667typedef ma_result (* ma_decoder_seek_to_pcm_frame_proc) (ma_decoder* pDecoder, ma_uint64 frameIndex);
4668typedef ma_result (* ma_decoder_uninit_proc) (ma_decoder* pDecoder);
4669typedef ma_uint64 (* ma_decoder_get_length_in_pcm_frames_proc)(ma_decoder* pDecoder);
4670
4671typedef struct
4672{
4673 ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */
4674 ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */
4675 ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */
4676 ma_channel channelMap[MA_MAX_CHANNELS];
4677 ma_channel_mix_mode channelMixMode;
4678 ma_dither_mode ditherMode;
4679 struct
4680 {
4681 ma_resample_algorithm algorithm;
4682 struct
4683 {
4684 ma_uint32 lpfCount;
4685 } linear;
4686 struct
4687 {
4688 int quality;
4689 } speex;
4690 } resampling;
4691 ma_allocation_callbacks allocationCallbacks;
4692} ma_decoder_config;
4693
4694struct ma_decoder
4695{
4696 ma_decoder_read_proc onRead;
4697 ma_decoder_seek_proc onSeek;
4698 void* pUserData;
4699 ma_uint64 readPointer; /* Used for returning back to a previous position after analysing the stream or whatnot. */
4700 ma_format internalFormat;
4701 ma_uint32 internalChannels;
4702 ma_uint32 internalSampleRate;
4703 ma_channel internalChannelMap[MA_MAX_CHANNELS];
4704 ma_format outputFormat;
4705 ma_uint32 outputChannels;
4706 ma_uint32 outputSampleRate;
4707 ma_channel outputChannelMap[MA_MAX_CHANNELS];
4708 ma_data_converter converter; /* <-- Data conversion is achieved by running frames through this. */
4709 ma_allocation_callbacks allocationCallbacks;
4710 ma_decoder_read_pcm_frames_proc onReadPCMFrames;
4711 ma_decoder_seek_to_pcm_frame_proc onSeekToPCMFrame;
4712 ma_decoder_uninit_proc onUninit;
4713 ma_decoder_get_length_in_pcm_frames_proc onGetLengthInPCMFrames;
4714 void* pInternalDecoder; /* <-- The drwav/drflac/stb_vorbis/etc. objects. */
4715 struct
4716 {
4717 const ma_uint8* pData;
4718 size_t dataSize;
4719 size_t currentReadPos;
4720 } memory; /* Only used for decoders that were opened against a block of memory. */
4721};
4722
4723ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate);
4724
4725ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
4726ma_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);
4727ma_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);
4728ma_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);
4729ma_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);
4730ma_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);
4731
4732ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
4733ma_result ma_decoder_init_memory_wav(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
4734ma_result ma_decoder_init_memory_flac(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
4735ma_result ma_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
4736ma_result ma_decoder_init_memory_mp3(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
4737ma_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);
4738
4739#ifndef MA_NO_STDIO
4740ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
4741ma_result ma_decoder_init_file_wav(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
4742ma_result ma_decoder_init_file_flac(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
4743ma_result ma_decoder_init_file_vorbis(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
4744ma_result ma_decoder_init_file_mp3(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
4745
4746ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
4747ma_result ma_decoder_init_file_wav_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
4748ma_result ma_decoder_init_file_flac_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
4749ma_result ma_decoder_init_file_vorbis_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
4750ma_result ma_decoder_init_file_mp3_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
4751#endif
4752
4753ma_result ma_decoder_uninit(ma_decoder* pDecoder);
4754
4755/*
4756Retrieves the length of the decoder in PCM frames.
4757
4758Do not call this on streams of an undefined length, such as internet radio.
4759
4760If the length is unknown or an error occurs, 0 will be returned.
4761
4762This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio
4763uses internally.
4764
4765For MP3's, this will decode the entire file. Do not call this in time critical scenarios.
4766
4767This function is not thread safe without your own synchronization.
4768*/
4769ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder);
4770
4771/*
4772Reads PCM frames from the given decoder.
4773
4774This is not thread safe without your own synchronization.
4775*/
4776ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount);
4777
4778/*
4779Seeks to a PCM frame based on it's absolute index.
4780
4781This is not thread safe without your own synchronization.
4782*/
4783ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex);
4784
4785/*
4786Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input,
4787pConfig should be set to what you want. On output it will be set to what you got.
4788*/
4789#ifndef MA_NO_STDIO
4790ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppDataOut);
4791#endif
4792ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppDataOut);
4793
4794#endif /* MA_NO_DECODING */
4795
4796
4797/************************************************************************************************************************************************************
4798
4799Generation
4800
4801************************************************************************************************************************************************************/
4802typedef enum
4803{
4804 ma_waveform_type_sine,
4805 ma_waveform_type_square,
4806 ma_waveform_type_triangle,
4807 ma_waveform_type_sawtooth
4808} ma_waveform_type;
4809
4810typedef struct
4811{
4812 ma_waveform_type type;
4813 double amplitude;
4814 double frequency;
4815 double deltaTime;
4816 double time;
4817} ma_waveform;
4818
4819ma_result ma_waveform_init(ma_waveform_type type, double amplitude, double frequency, ma_uint32 sampleRate, ma_waveform* pWaveform);
4820ma_uint64 ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels);
4821ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude);
4822ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency);
4823ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate);
4824
4825#ifdef __cplusplus
4826}
4827#endif
4828#endif /* miniaudio_h */
4829
4830
4831
4832/************************************************************************************************************************************************************
4833*************************************************************************************************************************************************************
4834
4835IMPLEMENTATION
4836
4837*************************************************************************************************************************************************************
4838************************************************************************************************************************************************************/
4839#if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
4840#include <assert.h>
4841#include <limits.h> /* For INT_MAX */
4842#include <math.h> /* sin(), etc. */
4843
4844#if !defined(MA_NO_STDIO) || defined(MA_DEBUG_OUTPUT)
4845 #include <stdio.h>
4846 #if !defined(_MSC_VER) && !defined(__DMC__)
4847 #include <strings.h> /* For strcasecmp(). */
4848 #include <wchar.h> /* For wcslen(), wcsrtombs() */
4849 #endif
4850#endif
4851
4852#ifdef MA_WIN32
4853#include <windows.h>
4854#include <objbase.h>
4855#include <mmreg.h>
4856#include <mmsystem.h>
4857#else
4858#include <stdlib.h> /* For malloc(), free(), wcstombs(). */
4859#include <string.h> /* For memset() */
4860#endif
4861
4862#if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
4863#include <mach/mach_time.h> /* For mach_absolute_time() */
4864#endif
4865
4866#ifdef MA_POSIX
4867#include <sys/time.h>
4868#include <sys/types.h>
4869#include <unistd.h>
4870#include <dlfcn.h>
4871#endif
4872
4873#ifdef MA_EMSCRIPTEN
4874#include <emscripten/emscripten.h>
4875#endif
4876
4877#if !defined(MA_64BIT) && !defined(MA_32BIT)
4878#ifdef _WIN32
4879#ifdef _WIN64
4880#define MA_64BIT
4881#else
4882#define MA_32BIT
4883#endif
4884#endif
4885#endif
4886
4887#if !defined(MA_64BIT) && !defined(MA_32BIT)
4888#ifdef __GNUC__
4889#ifdef __LP64__
4890#define MA_64BIT
4891#else
4892#define MA_32BIT
4893#endif
4894#endif
4895#endif
4896
4897#if !defined(MA_64BIT) && !defined(MA_32BIT)
4898#include <stdint.h>
4899#if INTPTR_MAX == INT64_MAX
4900#define MA_64BIT
4901#else
4902#define MA_32BIT
4903#endif
4904#endif
4905
4906/* Architecture Detection */
4907#if defined(__x86_64__) || defined(_M_X64)
4908#define MA_X64
4909#elif defined(__i386) || defined(_M_IX86)
4910#define MA_X86
4911#elif defined(__arm__) || defined(_M_ARM)
4912#define MA_ARM
4913#endif
4914
4915/* Cannot currently support AVX-512 if AVX is disabled. */
4916#if !defined(MA_NO_AVX512) && defined(MA_NO_AVX2)
4917#define MA_NO_AVX512
4918#endif
4919
4920/* Intrinsics Support */
4921#if defined(MA_X64) || defined(MA_X86)
4922 #if defined(_MSC_VER) && !defined(__clang__)
4923 /* MSVC. */
4924 #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */
4925 #define MA_SUPPORT_SSE2
4926 #endif
4927 /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */
4928 /* #define MA_SUPPORT_AVX*/
4929 /*#endif*/
4930 #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */
4931 #define MA_SUPPORT_AVX2
4932 #endif
4933 #if _MSC_VER >= 1910 && !defined(MA_NO_AVX512) /* 2017 */
4934 #define MA_SUPPORT_AVX512
4935 #endif
4936 #else
4937 /* Assume GNUC-style. */
4938 #if defined(__SSE2__) && !defined(MA_NO_SSE2)
4939 #define MA_SUPPORT_SSE2
4940 #endif
4941 /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/
4942 /* #define MA_SUPPORT_AVX*/
4943 /*#endif*/
4944 #if defined(__AVX2__) && !defined(MA_NO_AVX2)
4945 #define MA_SUPPORT_AVX2
4946 #endif
4947 #if defined(__AVX512F__) && !defined(MA_NO_AVX512)
4948 #define MA_SUPPORT_AVX512
4949 #endif
4950 #endif
4951
4952 /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */
4953 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
4954 #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include(<emmintrin.h>)
4955 #define MA_SUPPORT_SSE2
4956 #endif
4957 /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include(<immintrin.h>)*/
4958 /* #define MA_SUPPORT_AVX*/
4959 /*#endif*/
4960 #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include(<immintrin.h>)
4961 #define MA_SUPPORT_AVX2
4962 #endif
4963 #if !defined(MA_SUPPORT_AVX512) && !defined(MA_NO_AVX512) && __has_include(<zmmintrin.h>)
4964 #define MA_SUPPORT_AVX512
4965 #endif
4966 #endif
4967
4968 #if defined(MA_SUPPORT_AVX512)
4969 #include <immintrin.h> /* Not a mistake. Intentionally including <immintrin.h> instead of <zmmintrin.h> because otherwise the compiler will complain. */
4970 #elif defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX)
4971 #include <immintrin.h>
4972 #elif defined(MA_SUPPORT_SSE2)
4973 #include <emmintrin.h>
4974 #endif
4975#endif
4976
4977#if defined(MA_ARM)
4978 #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
4979 #define MA_SUPPORT_NEON
4980 #endif
4981
4982 /* Fall back to looking for the #include file. */
4983 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
4984 #if !defined(MA_SUPPORT_NEON) && !defined(MA_NO_NEON) && __has_include(<arm_neon.h>)
4985 #define MA_SUPPORT_NEON
4986 #endif
4987 #endif
4988
4989 #if defined(MA_SUPPORT_NEON)
4990 #include <arm_neon.h>
4991 #endif
4992#endif
4993
4994/* Begin globally disabled warnings. */
4995#if defined(_MSC_VER)
4996 #pragma warning(push)
4997 #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */
4998#endif
4999
5000#if defined(MA_X64) || defined(MA_X86)
5001 #if defined(_MSC_VER) && !defined(__clang__)
5002 #if _MSC_VER >= 1400
5003 #include <intrin.h>
5004 static MA_INLINE void ma_cpuid(int info[4], int fid)
5005 {
5006 __cpuid(info, fid);
5007 }
5008 #else
5009 #define MA_NO_CPUID
5010 #endif
5011
5012 #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219)
5013 static MA_INLINE unsigned __int64 ma_xgetbv(int reg)
5014 {
5015 return _xgetbv(reg);
5016 }
5017 #else
5018 #define MA_NO_XGETBV
5019 #endif
5020 #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID)
5021 static MA_INLINE void ma_cpuid(int info[4], int fid)
5022 {
5023 /*
5024 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
5025 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
5026 supporting different assembly dialects.
5027
5028 What's basically happening is that we're saving and restoring the ebx register manually.
5029 */
5030 #if defined(DRFLAC_X86) && defined(__PIC__)
5031 __asm__ __volatile__ (
5032 "xchg{l} {%%}ebx, %k1;"
5033 "cpuid;"
5034 "xchg{l} {%%}ebx, %k1;"
5035 : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
5036 );
5037 #else
5038 __asm__ __volatile__ (
5039 "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
5040 );
5041 #endif
5042 }
5043
5044 static MA_INLINE ma_uint64 ma_xgetbv(int reg)
5045 {
5046 unsigned int hi;
5047 unsigned int lo;
5048
5049 __asm__ __volatile__ (
5050 "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg)
5051 );
5052
5053 return ((ma_uint64)hi << 32) | (ma_uint64)lo;
5054 }
5055 #else
5056 #define MA_NO_CPUID
5057 #define MA_NO_XGETBV
5058 #endif
5059#else
5060 #define MA_NO_CPUID
5061 #define MA_NO_XGETBV
5062#endif
5063
5064static MA_INLINE ma_bool32 ma_has_sse2()
5065{
5066#if defined(MA_SUPPORT_SSE2)
5067 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2)
5068 #if defined(MA_X64)
5069 return MA_TRUE; /* 64-bit targets always support SSE2. */
5070 #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
5071 return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */
5072 #else
5073 #if defined(MA_NO_CPUID)
5074 return MA_FALSE;
5075 #else
5076 int info[4];
5077 ma_cpuid(info, 1);
5078 return (info[3] & (1 << 26)) != 0;
5079 #endif
5080 #endif
5081 #else
5082 return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */
5083 #endif
5084#else
5085 return MA_FALSE; /* No compiler support. */
5086#endif
5087}
5088
5089#if 0
5090static MA_INLINE ma_bool32 ma_has_avx()
5091{
5092#if defined(MA_SUPPORT_AVX)
5093 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX)
5094 #if defined(_AVX_) || defined(__AVX__)
5095 return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */
5096 #else
5097 /* AVX requires both CPU and OS support. */
5098 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
5099 return MA_FALSE;
5100 #else
5101 int info[4];
5102 ma_cpuid(info, 1);
5103 if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) {
5104 ma_uint64 xrc = ma_xgetbv(0);
5105 if ((xrc & 0x06) == 0x06) {
5106 return MA_TRUE;
5107 } else {
5108 return MA_FALSE;
5109 }
5110 } else {
5111 return MA_FALSE;
5112 }
5113 #endif
5114 #endif
5115 #else
5116 return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */
5117 #endif
5118#else
5119 return MA_FALSE; /* No compiler support. */
5120#endif
5121}
5122#endif
5123
5124static MA_INLINE ma_bool32 ma_has_avx2()
5125{
5126#if defined(MA_SUPPORT_AVX2)
5127 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2)
5128 #if defined(_AVX2_) || defined(__AVX2__)
5129 return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */
5130 #else
5131 /* AVX2 requires both CPU and OS support. */
5132 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
5133 return MA_FALSE;
5134 #else
5135 int info1[4];
5136 int info7[4];
5137 ma_cpuid(info1, 1);
5138 ma_cpuid(info7, 7);
5139 if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) {
5140 ma_uint64 xrc = ma_xgetbv(0);
5141 if ((xrc & 0x06) == 0x06) {
5142 return MA_TRUE;
5143 } else {
5144 return MA_FALSE;
5145 }
5146 } else {
5147 return MA_FALSE;
5148 }
5149 #endif
5150 #endif
5151 #else
5152 return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */
5153 #endif
5154#else
5155 return MA_FALSE; /* No compiler support. */
5156#endif
5157}
5158
5159static MA_INLINE ma_bool32 ma_has_avx512f()
5160{
5161#if defined(MA_SUPPORT_AVX512)
5162 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX512)
5163 #if defined(__AVX512F__)
5164 return MA_TRUE; /* If the compiler is allowed to freely generate AVX-512F code we can assume support. */
5165 #else
5166 /* AVX-512 requires both CPU and OS support. */
5167 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
5168 return MA_FALSE;
5169 #else
5170 int info1[4];
5171 int info7[4];
5172 ma_cpuid(info1, 1);
5173 ma_cpuid(info7, 7);
5174 if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 16)) != 0)) {
5175 ma_uint64 xrc = ma_xgetbv(0);
5176 if ((xrc & 0xE6) == 0xE6) {
5177 return MA_TRUE;
5178 } else {
5179 return MA_FALSE;
5180 }
5181 } else {
5182 return MA_FALSE;
5183 }
5184 #endif
5185 #endif
5186 #else
5187 return MA_FALSE; /* AVX-512F is only supported on x86 and x64 architectures. */
5188 #endif
5189#else
5190 return MA_FALSE; /* No compiler support. */
5191#endif
5192}
5193
5194static MA_INLINE ma_bool32 ma_has_neon()
5195{
5196#if defined(MA_SUPPORT_NEON)
5197 #if defined(MA_ARM) && !defined(MA_NO_NEON)
5198 #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
5199 return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */
5200 #else
5201 /* TODO: Runtime check. */
5202 return MA_FALSE;
5203 #endif
5204 #else
5205 return MA_FALSE; /* NEON is only supported on ARM architectures. */
5206 #endif
5207#else
5208 return MA_FALSE; /* No compiler support. */
5209#endif
5210}
5211
5212#define MA_SIMD_NONE 0
5213#define MA_SIMD_SSE2 1
5214#define MA_SIMD_AVX2 2
5215#define MA_SIMD_NEON 3
5216
5217#ifndef MA_PREFERRED_SIMD
5218 # if defined(MA_SUPPORT_SSE2) && defined(MA_PREFER_SSE2)
5219 #define MA_PREFERRED_SIMD MA_SIMD_SSE2
5220 #elif defined(MA_SUPPORT_AVX2) && defined(MA_PREFER_AVX2)
5221 #define MA_PREFERRED_SIMD MA_SIMD_AVX2
5222 #elif defined(MA_SUPPORT_NEON) && defined(MA_PREFER_NEON)
5223 #define MA_PREFERRED_SIMD MA_SIMD_NEON
5224 #else
5225 #define MA_PREFERRED_SIMD MA_SIMD_NONE
5226 #endif
5227#endif
5228
5229
5230static MA_INLINE ma_bool32 ma_is_little_endian()
5231{
5232#if defined(MA_X86) || defined(MA_X64)
5233 return MA_TRUE;
5234#else
5235 int n = 1;
5236 return (*(char*)&n) == 1;
5237#endif
5238}
5239
5240static MA_INLINE ma_bool32 ma_is_big_endian()
5241{
5242 return !ma_is_little_endian();
5243}
5244
5245
5246#ifndef MA_COINIT_VALUE
5247#define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */
5248#endif
5249
5250
5251
5252#ifndef MA_PI
5253#define MA_PI 3.14159265358979323846264f
5254#endif
5255#ifndef MA_PI_D
5256#define MA_PI_D 3.14159265358979323846264
5257#endif
5258#ifndef MA_TAU
5259#define MA_TAU 6.28318530717958647693f
5260#endif
5261#ifndef MA_TAU_D
5262#define MA_TAU_D 6.28318530717958647693
5263#endif
5264
5265
5266/* The default format when ma_format_unknown (0) is requested when initializing a device. */
5267#ifndef MA_DEFAULT_FORMAT
5268#define MA_DEFAULT_FORMAT ma_format_f32
5269#endif
5270
5271/* The default channel count to use when 0 is used when initializing a device. */
5272#ifndef MA_DEFAULT_CHANNELS
5273#define MA_DEFAULT_CHANNELS 2
5274#endif
5275
5276/* The default sample rate to use when 0 is used when initializing a device. */
5277#ifndef MA_DEFAULT_SAMPLE_RATE
5278#define MA_DEFAULT_SAMPLE_RATE 48000
5279#endif
5280
5281/* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */
5282#ifndef MA_DEFAULT_PERIODS
5283#define MA_DEFAULT_PERIODS 3
5284#endif
5285
5286/* The default period size in milliseconds for low latency mode. */
5287#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY
5288#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 10
5289#endif
5290
5291/* The default buffer size in milliseconds for conservative mode. */
5292#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE
5293#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100
5294#endif
5295
5296/* The default LPF count for linear resampling. Note that this is clamped to MA_MAX_RESAMPLER_LPF_FILTERS. */
5297#ifndef MA_DEFAULT_RESAMPLER_LPF_FILTERS
5298 #if MA_MAX_RESAMPLER_LPF_FILTERS >= 2
5299 #define MA_DEFAULT_RESAMPLER_LPF_FILTERS 2
5300 #else
5301 #define MA_DEFAULT_RESAMPLER_LPF_FILTERS MA_MAX_RESAMPLER_LPF_FILTERS
5302 #endif
5303#endif
5304
5305
5306/* Standard sample rates, in order of priority. */
5307ma_uint32 g_maStandardSampleRatePriorities[] = {
5308 MA_SAMPLE_RATE_48000, /* Most common */
5309 MA_SAMPLE_RATE_44100,
5310
5311 MA_SAMPLE_RATE_32000, /* Lows */
5312 MA_SAMPLE_RATE_24000,
5313 MA_SAMPLE_RATE_22050,
5314
5315 MA_SAMPLE_RATE_88200, /* Highs */
5316 MA_SAMPLE_RATE_96000,
5317 MA_SAMPLE_RATE_176400,
5318 MA_SAMPLE_RATE_192000,
5319
5320 MA_SAMPLE_RATE_16000, /* Extreme lows */
5321 MA_SAMPLE_RATE_11025,
5322 MA_SAMPLE_RATE_8000,
5323
5324 MA_SAMPLE_RATE_352800, /* Extreme highs */
5325 MA_SAMPLE_RATE_384000
5326};
5327
5328ma_format g_maFormatPriorities[] = {
5329 ma_format_s16, /* Most common */
5330 ma_format_f32,
5331
5332 /*ma_format_s24_32,*/ /* Clean alignment */
5333 ma_format_s32,
5334
5335 ma_format_s24, /* Unclean alignment */
5336
5337 ma_format_u8 /* Low quality */
5338};
5339
5340
5341
5342/******************************************************************************
5343
5344Standard Library Stuff
5345
5346******************************************************************************/
5347#ifndef MA_MALLOC
5348#ifdef MA_WIN32
5349#define MA_MALLOC(sz) HeapAlloc(GetProcessHeap(), 0, (sz))
5350#else
5351#define MA_MALLOC(sz) malloc((sz))
5352#endif
5353#endif
5354
5355#ifndef MA_REALLOC
5356#ifdef MA_WIN32
5357#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)))
5358#else
5359#define MA_REALLOC(p, sz) realloc((p), (sz))
5360#endif
5361#endif
5362
5363#ifndef MA_FREE
5364#ifdef MA_WIN32
5365#define MA_FREE(p) HeapFree(GetProcessHeap(), 0, (p))
5366#else
5367#define MA_FREE(p) free((p))
5368#endif
5369#endif
5370
5371#ifndef MA_ZERO_MEMORY
5372#ifdef MA_WIN32
5373#define MA_ZERO_MEMORY(p, sz) ZeroMemory((p), (sz))
5374#else
5375#define MA_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
5376#endif
5377#endif
5378
5379#ifndef MA_COPY_MEMORY
5380#ifdef MA_WIN32
5381#define MA_COPY_MEMORY(dst, src, sz) CopyMemory((dst), (src), (sz))
5382#else
5383#define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
5384#endif
5385#endif
5386
5387#ifndef MA_ASSERT
5388#ifdef MA_WIN32
5389#define MA_ASSERT(condition) assert(condition)
5390#else
5391#define MA_ASSERT(condition) assert(condition)
5392#endif
5393#endif
5394
5395#define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p)))
5396
5397#define ma_countof(x) (sizeof(x) / sizeof(x[0]))
5398#define ma_max(x, y) (((x) > (y)) ? (x) : (y))
5399#define ma_min(x, y) (((x) < (y)) ? (x) : (y))
5400#define ma_abs(x) (((x) > 0) ? (x) : -(x))
5401#define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi)))
5402#define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset))
5403
5404#define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels))
5405
5406static MA_INLINE double ma_sin(double x)
5407{
5408 /* TODO: Implement custom sin(x). */
5409 return sin(x);
5410}
5411
5412static MA_INLINE double ma_cos(double x)
5413{
5414 return ma_sin((MA_PI*0.5) - x);
5415}
5416
5417static MA_INLINE double ma_log(double x)
5418{
5419 /* TODO: Implement custom log(x). */
5420 return log(x);
5421}
5422
5423static MA_INLINE double ma_pow(double x, double y)
5424{
5425 /* TODO: Implement custom pow(x, y). */
5426 return pow(x, y);
5427}
5428
5429static MA_INLINE double ma_log10(double x)
5430{
5431 return ma_log(x) * 0.43429448190325182765;
5432}
5433
5434static MA_INLINE float ma_powf(float x, float y)
5435{
5436 return (float)ma_pow((double)x, (double)y);
5437}
5438
5439static MA_INLINE float ma_log10f(float x)
5440{
5441 return (float)ma_log10((double)x);
5442}
5443
5444
5445/*
5446Return Values:
5447 0: Success
5448 22: EINVAL
5449 34: ERANGE
5450
5451Not using symbolic constants for errors because I want to avoid #including errno.h
5452*/
5453int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
5454{
5455 size_t i;
5456
5457 if (dst == 0) {
5458 return 22;
5459 }
5460 if (dstSizeInBytes == 0) {
5461 return 34;
5462 }
5463 if (src == 0) {
5464 dst[0] = '\0';
5465 return 22;
5466 }
5467
5468 for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
5469 dst[i] = src[i];
5470 }
5471
5472 if (i < dstSizeInBytes) {
5473 dst[i] = '\0';
5474 return 0;
5475 }
5476
5477 dst[0] = '\0';
5478 return 34;
5479}
5480
5481int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
5482{
5483 size_t maxcount;
5484 size_t i;
5485
5486 if (dst == 0) {
5487 return 22;
5488 }
5489 if (dstSizeInBytes == 0) {
5490 return 34;
5491 }
5492 if (src == 0) {
5493 dst[0] = '\0';
5494 return 22;
5495 }
5496
5497 maxcount = count;
5498 if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */
5499 maxcount = dstSizeInBytes - 1;
5500 }
5501
5502 for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
5503 dst[i] = src[i];
5504 }
5505
5506 if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
5507 dst[i] = '\0';
5508 return 0;
5509 }
5510
5511 dst[0] = '\0';
5512 return 34;
5513}
5514
5515int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
5516{
5517 char* dstorig;
5518
5519 if (dst == 0) {
5520 return 22;
5521 }
5522 if (dstSizeInBytes == 0) {
5523 return 34;
5524 }
5525 if (src == 0) {
5526 dst[0] = '\0';
5527 return 22;
5528 }
5529
5530 dstorig = dst;
5531
5532 while (dstSizeInBytes > 0 && dst[0] != '\0') {
5533 dst += 1;
5534 dstSizeInBytes -= 1;
5535 }
5536
5537 if (dstSizeInBytes == 0) {
5538 return 22; /* Unterminated. */
5539 }
5540
5541
5542 while (dstSizeInBytes > 0 && src[0] != '\0') {
5543 *dst++ = *src++;
5544 dstSizeInBytes -= 1;
5545 }
5546
5547 if (dstSizeInBytes > 0) {
5548 dst[0] = '\0';
5549 } else {
5550 dstorig[0] = '\0';
5551 return 34;
5552 }
5553
5554 return 0;
5555}
5556
5557int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
5558{
5559 char* dstorig;
5560
5561 if (dst == 0) {
5562 return 22;
5563 }
5564 if (dstSizeInBytes == 0) {
5565 return 34;
5566 }
5567 if (src == 0) {
5568 return 22;
5569 }
5570
5571 dstorig = dst;
5572
5573 while (dstSizeInBytes > 0 && dst[0] != '\0') {
5574 dst += 1;
5575 dstSizeInBytes -= 1;
5576 }
5577
5578 if (dstSizeInBytes == 0) {
5579 return 22; /* Unterminated. */
5580 }
5581
5582
5583 if (count == ((size_t)-1)) { /* _TRUNCATE */
5584 count = dstSizeInBytes - 1;
5585 }
5586
5587 while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) {
5588 *dst++ = *src++;
5589 dstSizeInBytes -= 1;
5590 count -= 1;
5591 }
5592
5593 if (dstSizeInBytes > 0) {
5594 dst[0] = '\0';
5595 } else {
5596 dstorig[0] = '\0';
5597 return 34;
5598 }
5599
5600 return 0;
5601}
5602
5603int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix)
5604{
5605 int sign;
5606 unsigned int valueU;
5607 char* dstEnd;
5608
5609 if (dst == NULL || dstSizeInBytes == 0) {
5610 return 22;
5611 }
5612 if (radix < 2 || radix > 36) {
5613 dst[0] = '\0';
5614 return 22;
5615 }
5616
5617 sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */
5618
5619 if (value < 0) {
5620 valueU = -value;
5621 } else {
5622 valueU = value;
5623 }
5624
5625 dstEnd = dst;
5626 do
5627 {
5628 int remainder = valueU % radix;
5629 if (remainder > 9) {
5630 *dstEnd = (char)((remainder - 10) + 'a');
5631 } else {
5632 *dstEnd = (char)(remainder + '0');
5633 }
5634
5635 dstEnd += 1;
5636 dstSizeInBytes -= 1;
5637 valueU /= radix;
5638 } while (dstSizeInBytes > 0 && valueU > 0);
5639
5640 if (dstSizeInBytes == 0) {
5641 dst[0] = '\0';
5642 return 22; /* Ran out of room in the output buffer. */
5643 }
5644
5645 if (sign < 0) {
5646 *dstEnd++ = '-';
5647 dstSizeInBytes -= 1;
5648 }
5649
5650 if (dstSizeInBytes == 0) {
5651 dst[0] = '\0';
5652 return 22; /* Ran out of room in the output buffer. */
5653 }
5654
5655 *dstEnd = '\0';
5656
5657
5658 /* At this point the string will be reversed. */
5659 dstEnd -= 1;
5660 while (dst < dstEnd) {
5661 char temp = *dst;
5662 *dst = *dstEnd;
5663 *dstEnd = temp;
5664
5665 dst += 1;
5666 dstEnd -= 1;
5667 }
5668
5669 return 0;
5670}
5671
5672int ma_strcmp(const char* str1, const char* str2)
5673{
5674 if (str1 == str2) return 0;
5675
5676 /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */
5677 if (str1 == NULL) return -1;
5678 if (str2 == NULL) return 1;
5679
5680 for (;;) {
5681 if (str1[0] == '\0') {
5682 break;
5683 }
5684 if (str1[0] != str2[0]) {
5685 break;
5686 }
5687
5688 str1 += 1;
5689 str2 += 1;
5690 }
5691
5692 return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0];
5693}
5694
5695int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB)
5696{
5697 int result;
5698
5699 result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1);
5700 if (result != 0) {
5701 return result;
5702 }
5703
5704 result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1);
5705 if (result != 0) {
5706 return result;
5707 }
5708
5709 return result;
5710}
5711
5712char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks)
5713{
5714 size_t sz = strlen(src)+1;
5715 char* dst = (char*)ma_malloc(sz, pAllocationCallbacks);
5716 if (dst == NULL) {
5717 return NULL;
5718 }
5719
5720 ma_strcpy_s(dst, sz, src);
5721
5722 return dst;
5723}
5724
5725
5726static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes)
5727{
5728#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX
5729 MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes);
5730#else
5731 while (sizeInBytes > 0) {
5732 ma_uint64 bytesToCopyNow = sizeInBytes;
5733 if (bytesToCopyNow > MA_SIZE_MAX) {
5734 bytesToCopyNow = MA_SIZE_MAX;
5735 }
5736
5737 MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */
5738
5739 sizeInBytes -= bytesToCopyNow;
5740 dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow);
5741 src = (const void*)((const ma_uint8*)src + bytesToCopyNow);
5742 }
5743#endif
5744}
5745
5746static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes)
5747{
5748#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX
5749 MA_ZERO_MEMORY(dst, (size_t)sizeInBytes);
5750#else
5751 while (sizeInBytes > 0) {
5752 ma_uint64 bytesToZeroNow = sizeInBytes;
5753 if (bytesToZeroNow > MA_SIZE_MAX) {
5754 bytesToZeroNow = MA_SIZE_MAX;
5755 }
5756
5757 MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */
5758
5759 sizeInBytes -= bytesToZeroNow;
5760 dst = (void*)((ma_uint8*)dst + bytesToZeroNow);
5761 }
5762#endif
5763}
5764
5765
5766/* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */
5767static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x)
5768{
5769 x--;
5770 x |= x >> 1;
5771 x |= x >> 2;
5772 x |= x >> 4;
5773 x |= x >> 8;
5774 x |= x >> 16;
5775 x++;
5776
5777 return x;
5778}
5779
5780static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x)
5781{
5782 return ma_next_power_of_2(x) >> 1;
5783}
5784
5785static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x)
5786{
5787 unsigned int prev = ma_prev_power_of_2(x);
5788 unsigned int next = ma_next_power_of_2(x);
5789 if ((next - x) > (x - prev)) {
5790 return prev;
5791 } else {
5792 return next;
5793 }
5794}
5795
5796static MA_INLINE unsigned int ma_count_set_bits(unsigned int x)
5797{
5798 unsigned int count = 0;
5799 while (x != 0) {
5800 if (x & 1) {
5801 count += 1;
5802 }
5803
5804 x = x >> 1;
5805 }
5806
5807 return count;
5808}
5809
5810
5811
5812/* Clamps an f32 sample to -1..1 */
5813static MA_INLINE float ma_clip_f32(float x)
5814{
5815 if (x < -1) return -1;
5816 if (x > +1) return +1;
5817 return x;
5818}
5819
5820static MA_INLINE float ma_mix_f32(float x, float y, float a)
5821{
5822 return x*(1-a) + y*a;
5823}
5824static MA_INLINE float ma_mix_f32_fast(float x, float y, float a)
5825{
5826 float r0 = (y - x);
5827 float r1 = r0*a;
5828 return x + r1;
5829 /*return x + (y - x)*a;*/
5830}
5831
5832
5833#if defined(MA_SUPPORT_SSE2)
5834static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a)
5835{
5836 return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a));
5837}
5838#endif
5839#if defined(MA_SUPPORT_AVX2)
5840static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a)
5841{
5842 return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a));
5843}
5844#endif
5845#if defined(MA_SUPPORT_AVX512)
5846static MA_INLINE __m512 ma_mix_f32_fast__avx512(__m512 x, __m512 y, __m512 a)
5847{
5848 return _mm512_add_ps(x, _mm512_mul_ps(_mm512_sub_ps(y, x), a));
5849}
5850#endif
5851#if defined(MA_SUPPORT_NEON)
5852static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a)
5853{
5854 return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a));
5855}
5856#endif
5857
5858
5859static MA_INLINE double ma_mix_f64(double x, double y, double a)
5860{
5861 return x*(1-a) + y*a;
5862}
5863static MA_INLINE double ma_mix_f64_fast(double x, double y, double a)
5864{
5865 return x + (y - x)*a;
5866}
5867
5868static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi)
5869{
5870 return lo + x*(hi-lo);
5871}
5872
5873
5874/*
5875Greatest common factor using Euclid's algorithm iteratively.
5876*/
5877static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b)
5878{
5879 for (;;) {
5880 if (b == 0) {
5881 break;
5882 } else {
5883 ma_uint32 t = a;
5884 a = b;
5885 b = t % a;
5886 }
5887 }
5888
5889 return a;
5890}
5891
5892
5893/*
5894Random Number Generation
5895
5896miniaudio uses the LCG random number generation algorithm. This is good enough for audio.
5897
5898Note that miniaudio's LCG implementation uses global state which is _not_ thread-local. When this is called across
5899multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough
5900for miniaudio's purposes.
5901*/
5902#define MA_LCG_M 2147483647
5903#define MA_LCG_A 48271
5904#define MA_LCG_C 0
5905static ma_int32 g_maLCG = 4321; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */
5906
5907static MA_INLINE void ma_seed(ma_int32 seed)
5908{
5909 g_maLCG = seed;
5910}
5911
5912static MA_INLINE ma_int32 ma_rand_s32()
5913{
5914 ma_int32 lcg = g_maLCG;
5915 ma_int32 r = (MA_LCG_A * lcg + MA_LCG_C) % MA_LCG_M;
5916 g_maLCG = r;
5917 return r;
5918}
5919
5920static MA_INLINE ma_uint32 ma_rand_u32()
5921{
5922 return (ma_uint32)ma_rand_s32();
5923}
5924
5925static MA_INLINE double ma_rand_f64()
5926{
5927 return ma_rand_s32() / (double)0x7FFFFFFF;
5928}
5929
5930static MA_INLINE float ma_rand_f32()
5931{
5932 return (float)ma_rand_f64();
5933}
5934
5935static MA_INLINE float ma_rand_range_f32(float lo, float hi)
5936{
5937 return ma_scale_to_range_f32(ma_rand_f32(), lo, hi);
5938}
5939
5940static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi)
5941{
5942 if (lo == hi) {
5943 return lo;
5944 }
5945
5946 return lo + ma_rand_u32() / (0xFFFFFFFF / (hi - lo + 1) + 1);
5947}
5948
5949
5950static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax)
5951{
5952 return ma_rand_range_f32(ditherMin, ditherMax);
5953}
5954
5955static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax)
5956{
5957 float a = ma_rand_range_f32(ditherMin, 0);
5958 float b = ma_rand_range_f32(0, ditherMax);
5959 return a + b;
5960}
5961
5962static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax)
5963{
5964 if (ditherMode == ma_dither_mode_rectangle) {
5965 return ma_dither_f32_rectangle(ditherMin, ditherMax);
5966 }
5967 if (ditherMode == ma_dither_mode_triangle) {
5968 return ma_dither_f32_triangle(ditherMin, ditherMax);
5969 }
5970
5971 return 0;
5972}
5973
5974static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax)
5975{
5976 if (ditherMode == ma_dither_mode_rectangle) {
5977 ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax);
5978 return a;
5979 }
5980 if (ditherMode == ma_dither_mode_triangle) {
5981 ma_int32 a = ma_rand_range_s32(ditherMin, 0);
5982 ma_int32 b = ma_rand_range_s32(0, ditherMax);
5983 return a + b;
5984 }
5985
5986 return 0;
5987}
5988
5989
5990/******************************************************************************
5991
5992Atomics
5993
5994******************************************************************************/
5995#if defined(__clang__)
5996 #if defined(__has_builtin)
5997 #if __has_builtin(__sync_swap)
5998 #define MA_HAS_SYNC_SWAP
5999 #endif
6000 #endif
6001#elif defined(__GNUC__)
6002 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC__ >= 7)
6003 #define MA_HAS_GNUC_ATOMICS
6004 #endif
6005#endif
6006
6007#if defined(_WIN32) && !defined(__GNUC__) && !defined(__clang__)
6008#define ma_memory_barrier() MemoryBarrier()
6009#define ma_atomic_exchange_32(a, b) InterlockedExchange((LONG*)a, (LONG)b)
6010#define ma_atomic_exchange_64(a, b) InterlockedExchange64((LONGLONG*)a, (LONGLONG)b)
6011#define ma_atomic_increment_32(a) InterlockedIncrement((LONG*)a)
6012#define ma_atomic_decrement_32(a) InterlockedDecrement((LONG*)a)
6013#else
6014#define ma_memory_barrier() __sync_synchronize()
6015#if defined(MA_HAS_SYNC_SWAP)
6016 #define ma_atomic_exchange_32(a, b) __sync_swap(a, b)
6017 #define ma_atomic_exchange_64(a, b) __sync_swap(a, b)
6018#elif defined(MA_HAS_GNUC_ATOMICS)
6019 #define ma_atomic_exchange_32(a, b) (void)__atomic_exchange_n(a, b, __ATOMIC_ACQ_REL)
6020 #define ma_atomic_exchange_64(a, b) (void)__atomic_exchange_n(a, b, __ATOMIC_ACQ_REL)
6021#else
6022 #define ma_atomic_exchange_32(a, b) __sync_synchronize(); (void)__sync_lock_test_and_set(a, b)
6023 #define ma_atomic_exchange_64(a, b) __sync_synchronize(); (void)__sync_lock_test_and_set(a, b)
6024#endif
6025#define ma_atomic_increment_32(a) __sync_add_and_fetch(a, 1)
6026#define ma_atomic_decrement_32(a) __sync_sub_and_fetch(a, 1)
6027#endif
6028
6029#ifdef MA_64BIT
6030#define ma_atomic_exchange_ptr ma_atomic_exchange_64
6031#endif
6032#ifdef MA_32BIT
6033#define ma_atomic_exchange_ptr ma_atomic_exchange_32
6034#endif
6035
6036
6037static void* ma__malloc_default(size_t sz, void* pUserData)
6038{
6039 (void)pUserData;
6040 return MA_MALLOC(sz);
6041}
6042
6043static void* ma__realloc_default(void* p, size_t sz, void* pUserData)
6044{
6045 (void)pUserData;
6046 return MA_REALLOC(p, sz);
6047}
6048
6049static void ma__free_default(void* p, void* pUserData)
6050{
6051 (void)pUserData;
6052 MA_FREE(p);
6053}
6054
6055
6056static void* ma__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
6057{
6058 if (pAllocationCallbacks == NULL) {
6059 return NULL;
6060 }
6061
6062 if (pAllocationCallbacks->onMalloc != NULL) {
6063 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
6064 }
6065
6066 /* Try using realloc(). */
6067 if (pAllocationCallbacks->onRealloc != NULL) {
6068 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
6069 }
6070
6071 return NULL;
6072}
6073
6074static void* ma__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks)
6075{
6076 if (pAllocationCallbacks == NULL) {
6077 return NULL;
6078 }
6079
6080 if (pAllocationCallbacks->onRealloc != NULL) {
6081 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
6082 }
6083
6084 /* Try emulating realloc() in terms of malloc()/free(). */
6085 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
6086 void* p2;
6087
6088 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
6089 if (p2 == NULL) {
6090 return NULL;
6091 }
6092
6093 if (p != NULL) {
6094 MA_COPY_MEMORY(p2, p, szOld);
6095 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
6096 }
6097
6098 return p2;
6099 }
6100
6101 return NULL;
6102}
6103
6104static MA_INLINE void* ma__calloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
6105{
6106 void* p = ma__malloc_from_callbacks(sz, pAllocationCallbacks);
6107 if (p != NULL) {
6108 MA_ZERO_MEMORY(p, sz);
6109 }
6110
6111 return p;
6112}
6113
6114static void ma__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
6115{
6116 if (p == NULL || pAllocationCallbacks == NULL) {
6117 return;
6118 }
6119
6120 if (pAllocationCallbacks->onFree != NULL) {
6121 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
6122 }
6123}
6124
6125static ma_allocation_callbacks ma_allocation_callbacks_init_default()
6126{
6127 ma_allocation_callbacks callbacks;
6128 callbacks.pUserData = NULL;
6129 callbacks.onMalloc = ma__malloc_default;
6130 callbacks.onRealloc = ma__realloc_default;
6131 callbacks.onFree = ma__free_default;
6132
6133 return callbacks;
6134}
6135
6136static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc)
6137{
6138 if (pDst == NULL) {
6139 return MA_INVALID_ARGS;
6140 }
6141
6142 if (pSrc == NULL) {
6143 *pDst = ma_allocation_callbacks_init_default();
6144 } else {
6145 if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) {
6146 *pDst = ma_allocation_callbacks_init_default();
6147 } else {
6148 if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) {
6149 return MA_INVALID_ARGS; /* Invalid allocation callbacks. */
6150 } else {
6151 *pDst = *pSrc;
6152 }
6153 }
6154 }
6155
6156 return MA_SUCCESS;
6157}
6158
6159
6160ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn)
6161{
6162 /* For robustness we're going to use a resampler object to calculate this since that already has a way of calculating this. */
6163 ma_result result;
6164 ma_uint64 frameCountOut;
6165 ma_resampler_config config;
6166 ma_resampler resampler;
6167
6168 config = ma_resampler_config_init(ma_format_s16, 1, sampleRateIn, sampleRateOut, ma_resample_algorithm_linear);
6169 result = ma_resampler_init(&config, &resampler);
6170 if (result != MA_SUCCESS) {
6171 return 0;
6172 }
6173
6174 frameCountOut = ma_resampler_get_expected_output_frame_count(&resampler, frameCountIn);
6175
6176 ma_resampler_uninit(&resampler);
6177 return frameCountOut;
6178}
6179
6180#ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE
6181#define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096
6182#endif
6183
6184/************************************************************************************************************************************************************
6185*************************************************************************************************************************************************************
6186
6187DEVICE I/O
6188==========
6189
6190*************************************************************************************************************************************************************
6191************************************************************************************************************************************************************/
6192#ifndef MA_NO_DEVICE_IO
6193/*
6194Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When
6195using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing
6196compile-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
6197disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am
6198not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere.
6199*/
6200/*#define MA_USE_RUNTIME_LINKING_FOR_PTHREAD*/
6201
6202/* Disable run-time linking on certain backends. */
6203#ifndef MA_NO_RUNTIME_LINKING
6204 #if defined(MA_ANDROID) || defined(MA_EMSCRIPTEN)
6205 #define MA_NO_RUNTIME_LINKING
6206 #endif
6207#endif
6208
6209/*
6210Check if we have the necessary development packages for each backend at the top so we can use this to determine whether or not
6211certain unused functions and variables can be excluded from the build to avoid warnings.
6212*/
6213#ifdef MA_ENABLE_WASAPI
6214 #define MA_HAS_WASAPI /* Every compiler should support WASAPI */
6215#endif
6216#ifdef MA_ENABLE_DSOUND
6217 #define MA_HAS_DSOUND /* Every compiler should support DirectSound. */
6218#endif
6219#ifdef MA_ENABLE_WINMM
6220 #define MA_HAS_WINMM /* Every compiler I'm aware of supports WinMM. */
6221#endif
6222#ifdef MA_ENABLE_ALSA
6223 #define MA_HAS_ALSA
6224 #ifdef MA_NO_RUNTIME_LINKING
6225 #ifdef __has_include
6226 #if !__has_include(<alsa/asoundlib.h>)
6227 #undef MA_HAS_ALSA
6228 #endif
6229 #endif
6230 #endif
6231#endif
6232#ifdef MA_ENABLE_PULSEAUDIO
6233 #define MA_HAS_PULSEAUDIO
6234 #ifdef MA_NO_RUNTIME_LINKING
6235 #ifdef __has_include
6236 #if !__has_include(<pulse/pulseaudio.h>)
6237 #undef MA_HAS_PULSEAUDIO
6238 #endif
6239 #endif
6240 #endif
6241#endif
6242#ifdef MA_ENABLE_JACK
6243 #define MA_HAS_JACK
6244 #ifdef MA_NO_RUNTIME_LINKING
6245 #ifdef __has_include
6246 #if !__has_include(<jack/jack.h>)
6247 #undef MA_HAS_JACK
6248 #endif
6249 #endif
6250 #endif
6251#endif
6252#ifdef MA_ENABLE_COREAUDIO
6253 #define MA_HAS_COREAUDIO
6254#endif
6255#ifdef MA_ENABLE_SNDIO
6256 #define MA_HAS_SNDIO
6257#endif
6258#ifdef MA_ENABLE_AUDIO4
6259 #define MA_HAS_AUDIO4
6260#endif
6261#ifdef MA_ENABLE_OSS
6262 #define MA_HAS_OSS
6263#endif
6264#ifdef MA_ENABLE_AAUDIO
6265 #define MA_HAS_AAUDIO
6266#endif
6267#ifdef MA_ENABLE_OPENSL
6268 #define MA_HAS_OPENSL
6269#endif
6270#ifdef MA_ENABLE_WEBAUDIO
6271 #define MA_HAS_WEBAUDIO
6272#endif
6273#ifdef MA_ENABLE_NULL
6274 #define MA_HAS_NULL /* Everything supports the null backend. */
6275#endif
6276
6277const char* ma_get_backend_name(ma_backend backend)
6278{
6279 switch (backend)
6280 {
6281 case ma_backend_wasapi: return "WASAPI";
6282 case ma_backend_dsound: return "DirectSound";
6283 case ma_backend_winmm: return "WinMM";
6284 case ma_backend_coreaudio: return "Core Audio";
6285 case ma_backend_sndio: return "sndio";
6286 case ma_backend_audio4: return "audio(4)";
6287 case ma_backend_oss: return "OSS";
6288 case ma_backend_pulseaudio: return "PulseAudio";
6289 case ma_backend_alsa: return "ALSA";
6290 case ma_backend_jack: return "JACK";
6291 case ma_backend_aaudio: return "AAudio";
6292 case ma_backend_opensl: return "OpenSL|ES";
6293 case ma_backend_webaudio: return "Web Audio";
6294 case ma_backend_null: return "Null";
6295 default: return "Unknown";
6296 }
6297}
6298
6299ma_bool32 ma_is_loopback_supported(ma_backend backend)
6300{
6301 switch (backend)
6302 {
6303 case ma_backend_wasapi: return MA_TRUE;
6304 case ma_backend_dsound: return MA_FALSE;
6305 case ma_backend_winmm: return MA_FALSE;
6306 case ma_backend_coreaudio: return MA_FALSE;
6307 case ma_backend_sndio: return MA_FALSE;
6308 case ma_backend_audio4: return MA_FALSE;
6309 case ma_backend_oss: return MA_FALSE;
6310 case ma_backend_pulseaudio: return MA_FALSE;
6311 case ma_backend_alsa: return MA_FALSE;
6312 case ma_backend_jack: return MA_FALSE;
6313 case ma_backend_aaudio: return MA_FALSE;
6314 case ma_backend_opensl: return MA_FALSE;
6315 case ma_backend_webaudio: return MA_FALSE;
6316 case ma_backend_null: return MA_FALSE;
6317 default: return MA_FALSE;
6318 }
6319}
6320
6321
6322
6323#ifdef MA_WIN32
6324 #define MA_THREADCALL WINAPI
6325 typedef unsigned long ma_thread_result;
6326#else
6327 #define MA_THREADCALL
6328 typedef void* ma_thread_result;
6329#endif
6330typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData);
6331
6332#ifdef MA_WIN32
6333typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit);
6334typedef void (WINAPI * MA_PFN_CoUninitialize)();
6335typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv);
6336typedef void (WINAPI * MA_PFN_CoTaskMemFree)(LPVOID pv);
6337typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(PROPVARIANT *pvar);
6338typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax);
6339
6340typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)();
6341typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)();
6342
6343/* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */
6344typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult);
6345typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey);
6346typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData);
6347#endif
6348
6349
6350#define MA_STATE_UNINITIALIZED 0
6351#define MA_STATE_STOPPED 1 /* The device's default state after initialization. */
6352#define MA_STATE_STARTED 2 /* The worker thread is in it's main loop waiting for the driver to request or deliver audio data. */
6353#define MA_STATE_STARTING 3 /* Transitioning from a stopped state to started. */
6354#define MA_STATE_STOPPING 4 /* Transitioning from a started state to stopped. */
6355
6356#define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device"
6357#define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device"
6358
6359
6360const char* ma_log_level_to_string(ma_uint32 logLevel)
6361{
6362 switch (logLevel)
6363 {
6364 case MA_LOG_LEVEL_VERBOSE: return "";
6365 case MA_LOG_LEVEL_INFO: return "INFO";
6366 case MA_LOG_LEVEL_WARNING: return "WARNING";
6367 case MA_LOG_LEVEL_ERROR: return "ERROR";
6368 default: return "ERROR";
6369 }
6370}
6371
6372/* Posts a log message. */
6373static void ma_post_log_message(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
6374{
6375 if (pContext == NULL) {
6376 return;
6377 }
6378
6379#if defined(MA_LOG_LEVEL)
6380 if (logLevel <= MA_LOG_LEVEL) {
6381 ma_log_proc onLog;
6382
6383 #if defined(MA_DEBUG_OUTPUT)
6384 if (logLevel <= MA_LOG_LEVEL) {
6385 printf("%s: %s\n", ma_log_level_to_string(logLevel), message);
6386 }
6387 #endif
6388
6389 onLog = pContext->logCallback;
6390 if (onLog) {
6391 onLog(pContext, pDevice, logLevel, message);
6392 }
6393 }
6394#endif
6395}
6396
6397/* Posts an log message. Throw a breakpoint in here if you're needing to debug. The return value is always "resultCode". */
6398static ma_result ma_context_post_error(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode)
6399{
6400 /* Derive the context from the device if necessary. */
6401 if (pContext == NULL) {
6402 if (pDevice != NULL) {
6403 pContext = pDevice->pContext;
6404 }
6405 }
6406
6407 ma_post_log_message(pContext, pDevice, logLevel, message);
6408 return resultCode;
6409}
6410
6411static ma_result ma_post_error(ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode)
6412{
6413 return ma_context_post_error(NULL, pDevice, logLevel, message, resultCode);
6414}
6415
6416
6417/*******************************************************************************
6418
6419Timing
6420
6421*******************************************************************************/
6422#ifdef MA_WIN32
6423LARGE_INTEGER g_ma_TimerFrequency = {{0}};
6424static void ma_timer_init(ma_timer* pTimer)
6425{
6426 LARGE_INTEGER counter;
6427
6428 if (g_ma_TimerFrequency.QuadPart == 0) {
6429 QueryPerformanceFrequency(&g_ma_TimerFrequency);
6430 }
6431
6432 QueryPerformanceCounter(&counter);
6433 pTimer->counter = counter.QuadPart;
6434}
6435
6436static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
6437{
6438 LARGE_INTEGER counter;
6439 if (!QueryPerformanceCounter(&counter)) {
6440 return 0;
6441 }
6442
6443 return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart;
6444}
6445#elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
6446ma_uint64 g_ma_TimerFrequency = 0;
6447static void ma_timer_init(ma_timer* pTimer)
6448{
6449 mach_timebase_info_data_t baseTime;
6450 mach_timebase_info(&baseTime);
6451 g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer;
6452
6453 pTimer->counter = mach_absolute_time();
6454}
6455
6456static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
6457{
6458 ma_uint64 newTimeCounter = mach_absolute_time();
6459 ma_uint64 oldTimeCounter = pTimer->counter;
6460
6461 return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency;
6462}
6463#elif defined(MA_EMSCRIPTEN)
6464static MA_INLINE void ma_timer_init(ma_timer* pTimer)
6465{
6466 pTimer->counterD = emscripten_get_now();
6467}
6468
6469static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)
6470{
6471 return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */
6472}
6473#else
6474#if _POSIX_C_SOURCE >= 199309L
6475#if defined(CLOCK_MONOTONIC)
6476 #define MA_CLOCK_ID CLOCK_MONOTONIC
6477#else
6478 #define MA_CLOCK_ID CLOCK_REALTIME
6479#endif
6480
6481static void ma_timer_init(ma_timer* pTimer)
6482{
6483 struct timespec newTime;
6484 clock_gettime(MA_CLOCK_ID, &newTime);
6485
6486 pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
6487}
6488
6489static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
6490{
6491 ma_uint64 newTimeCounter;
6492 ma_uint64 oldTimeCounter;
6493
6494 struct timespec newTime;
6495 clock_gettime(MA_CLOCK_ID, &newTime);
6496
6497 newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
6498 oldTimeCounter = pTimer->counter;
6499
6500 return (newTimeCounter - oldTimeCounter) / 1000000000.0;
6501}
6502#else
6503static void ma_timer_init(ma_timer* pTimer)
6504{
6505 struct timeval newTime;
6506 gettimeofday(&newTime, NULL);
6507
6508 pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
6509}
6510
6511static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
6512{
6513 ma_uint64 newTimeCounter;
6514 ma_uint64 oldTimeCounter;
6515
6516 struct timeval newTime;
6517 gettimeofday(&newTime, NULL);
6518
6519 newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
6520 oldTimeCounter = pTimer->counter;
6521
6522 return (newTimeCounter - oldTimeCounter) / 1000000.0;
6523}
6524#endif
6525#endif
6526
6527
6528/*******************************************************************************
6529
6530Dynamic Linking
6531
6532*******************************************************************************/
6533ma_handle ma_dlopen(ma_context* pContext, const char* filename)
6534{
6535 ma_handle handle;
6536
6537#if MA_LOG_LEVEL >= MA_LOG_LEVEL_VERBOSE
6538 if (pContext != NULL) {
6539 char message[256];
6540 ma_strappend(message, sizeof(message), "Loading library: ", filename);
6541 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, message);
6542 }
6543#endif
6544
6545#ifdef _WIN32
6546#ifdef MA_WIN32_DESKTOP
6547 handle = (ma_handle)LoadLibraryA(filename);
6548#else
6549 /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */
6550 WCHAR filenameW[4096];
6551 if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) {
6552 handle = NULL;
6553 } else {
6554 handle = (ma_handle)LoadPackagedLibrary(filenameW, 0);
6555 }
6556#endif
6557#else
6558 handle = (ma_handle)dlopen(filename, RTLD_NOW);
6559#endif
6560
6561 /*
6562 I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority
6563 backend is a deliberate design choice. Instead I'm logging it as an informational message.
6564 */
6565#if MA_LOG_LEVEL >= MA_LOG_LEVEL_INFO
6566 if (handle == NULL) {
6567 char message[256];
6568 ma_strappend(message, sizeof(message), "Failed to load library: ", filename);
6569 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, message);
6570 }
6571#endif
6572
6573 (void)pContext; /* It's possible for pContext to be unused. */
6574 return handle;
6575}
6576
6577void ma_dlclose(ma_context* pContext, ma_handle handle)
6578{
6579#ifdef _WIN32
6580 FreeLibrary((HMODULE)handle);
6581#else
6582 dlclose((void*)handle);
6583#endif
6584
6585 (void)pContext;
6586}
6587
6588ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symbol)
6589{
6590 ma_proc proc;
6591
6592#if MA_LOG_LEVEL >= MA_LOG_LEVEL_VERBOSE
6593 if (pContext != NULL) {
6594 char message[256];
6595 ma_strappend(message, sizeof(message), "Loading symbol: ", symbol);
6596 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, message);
6597 }
6598#endif
6599
6600#ifdef _WIN32
6601 proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol);
6602#else
6603#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
6604 #pragma GCC diagnostic push
6605 #pragma GCC diagnostic ignored "-Wpedantic"
6606#endif
6607 proc = (ma_proc)dlsym((void*)handle, symbol);
6608#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
6609 #pragma GCC diagnostic pop
6610#endif
6611#endif
6612
6613#if MA_LOG_LEVEL >= MA_LOG_LEVEL_WARNING
6614 if (handle == NULL) {
6615 char message[256];
6616 ma_strappend(message, sizeof(message), "Failed to load symbol: ", symbol);
6617 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_WARNING, message);
6618 }
6619#endif
6620
6621 (void)pContext; /* It's possible for pContext to be unused. */
6622 return proc;
6623}
6624
6625
6626/*******************************************************************************
6627
6628Threading
6629
6630*******************************************************************************/
6631#ifdef MA_WIN32
6632static int ma_thread_priority_to_win32(ma_thread_priority priority)
6633{
6634 switch (priority) {
6635 case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE;
6636 case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST;
6637 case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL;
6638 case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL;
6639 case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL;
6640 case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST;
6641 case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL;
6642 default: return THREAD_PRIORITY_NORMAL;
6643 }
6644}
6645
6646static ma_result ma_thread_create__win32(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData)
6647{
6648 pThread->win32.hThread = CreateThread(NULL, 0, entryProc, pData, 0, NULL);
6649 if (pThread->win32.hThread == NULL) {
6650 return MA_FAILED_TO_CREATE_THREAD;
6651 }
6652
6653 SetThreadPriority((HANDLE)pThread->win32.hThread, ma_thread_priority_to_win32(pContext->threadPriority));
6654
6655 return MA_SUCCESS;
6656}
6657
6658static void ma_thread_wait__win32(ma_thread* pThread)
6659{
6660 WaitForSingleObject(pThread->win32.hThread, INFINITE);
6661}
6662
6663static void ma_sleep__win32(ma_uint32 milliseconds)
6664{
6665 Sleep((DWORD)milliseconds);
6666}
6667
6668
6669static ma_result ma_mutex_init__win32(ma_context* pContext, ma_mutex* pMutex)
6670{
6671 (void)pContext;
6672
6673 pMutex->win32.hMutex = CreateEventA(NULL, FALSE, TRUE, NULL);
6674 if (pMutex->win32.hMutex == NULL) {
6675 return MA_FAILED_TO_CREATE_MUTEX;
6676 }
6677
6678 return MA_SUCCESS;
6679}
6680
6681static void ma_mutex_uninit__win32(ma_mutex* pMutex)
6682{
6683 CloseHandle(pMutex->win32.hMutex);
6684}
6685
6686static void ma_mutex_lock__win32(ma_mutex* pMutex)
6687{
6688 WaitForSingleObject(pMutex->win32.hMutex, INFINITE);
6689}
6690
6691static void ma_mutex_unlock__win32(ma_mutex* pMutex)
6692{
6693 SetEvent(pMutex->win32.hMutex);
6694}
6695
6696
6697static ma_result ma_event_init__win32(ma_context* pContext, ma_event* pEvent)
6698{
6699 (void)pContext;
6700
6701 pEvent->win32.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
6702 if (pEvent->win32.hEvent == NULL) {
6703 return MA_FAILED_TO_CREATE_EVENT;
6704 }
6705
6706 return MA_SUCCESS;
6707}
6708
6709static void ma_event_uninit__win32(ma_event* pEvent)
6710{
6711 CloseHandle(pEvent->win32.hEvent);
6712}
6713
6714static ma_bool32 ma_event_wait__win32(ma_event* pEvent)
6715{
6716 return WaitForSingleObject(pEvent->win32.hEvent, INFINITE) == WAIT_OBJECT_0;
6717}
6718
6719static ma_bool32 ma_event_signal__win32(ma_event* pEvent)
6720{
6721 return SetEvent(pEvent->win32.hEvent);
6722}
6723
6724
6725static ma_result ma_semaphore_init__win32(ma_context* pContext, int initialValue, ma_semaphore* pSemaphore)
6726{
6727 (void)pContext;
6728
6729 pSemaphore->win32.hSemaphore = CreateSemaphoreA(NULL, (LONG)initialValue, LONG_MAX, NULL);
6730 if (pSemaphore->win32.hSemaphore == NULL) {
6731 return MA_FAILED_TO_CREATE_SEMAPHORE;
6732 }
6733
6734 return MA_SUCCESS;
6735}
6736
6737static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore)
6738{
6739 CloseHandle((HANDLE)pSemaphore->win32.hSemaphore);
6740}
6741
6742static ma_bool32 ma_semaphore_wait__win32(ma_semaphore* pSemaphore)
6743{
6744 return WaitForSingleObject((HANDLE)pSemaphore->win32.hSemaphore, INFINITE) == WAIT_OBJECT_0;
6745}
6746
6747static ma_bool32 ma_semaphore_release__win32(ma_semaphore* pSemaphore)
6748{
6749 return ReleaseSemaphore((HANDLE)pSemaphore->win32.hSemaphore, 1, NULL) != 0;
6750}
6751#endif
6752
6753
6754#ifdef MA_POSIX
6755#include <sched.h>
6756
6757typedef int (* ma_pthread_create_proc)(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
6758typedef int (* ma_pthread_join_proc)(pthread_t thread, void **retval);
6759typedef int (* ma_pthread_mutex_init_proc)(pthread_mutex_t *__mutex, const pthread_mutexattr_t *__mutexattr);
6760typedef int (* ma_pthread_mutex_destroy_proc)(pthread_mutex_t *__mutex);
6761typedef int (* ma_pthread_mutex_lock_proc)(pthread_mutex_t *__mutex);
6762typedef int (* ma_pthread_mutex_unlock_proc)(pthread_mutex_t *__mutex);
6763typedef int (* ma_pthread_cond_init_proc)(pthread_cond_t *__restrict __cond, const pthread_condattr_t *__restrict __cond_attr);
6764typedef int (* ma_pthread_cond_destroy_proc)(pthread_cond_t *__cond);
6765typedef int (* ma_pthread_cond_signal_proc)(pthread_cond_t *__cond);
6766typedef int (* ma_pthread_cond_wait_proc)(pthread_cond_t *__restrict __cond, pthread_mutex_t *__restrict __mutex);
6767typedef int (* ma_pthread_attr_init_proc)(pthread_attr_t *attr);
6768typedef int (* ma_pthread_attr_destroy_proc)(pthread_attr_t *attr);
6769typedef int (* ma_pthread_attr_setschedpolicy_proc)(pthread_attr_t *attr, int policy);
6770typedef int (* ma_pthread_attr_getschedparam_proc)(const pthread_attr_t *attr, struct sched_param *param);
6771typedef int (* ma_pthread_attr_setschedparam_proc)(pthread_attr_t *attr, const struct sched_param *param);
6772
6773static ma_result ma_thread_create__posix(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData)
6774{
6775 int result;
6776 pthread_attr_t* pAttr = NULL;
6777
6778#if !defined(__EMSCRIPTEN__)
6779 /* Try setting the thread priority. It's not critical if anything fails here. */
6780 pthread_attr_t attr;
6781 if (((ma_pthread_attr_init_proc)pContext->posix.pthread_attr_init)(&attr) == 0) {
6782 int scheduler = -1;
6783 if (pContext->threadPriority == ma_thread_priority_idle) {
6784#ifdef SCHED_IDLE
6785 if (((ma_pthread_attr_setschedpolicy_proc)pContext->posix.pthread_attr_setschedpolicy)(&attr, SCHED_IDLE) == 0) {
6786 scheduler = SCHED_IDLE;
6787 }
6788#endif
6789 } else if (pContext->threadPriority == ma_thread_priority_realtime) {
6790#ifdef SCHED_FIFO
6791 if (((ma_pthread_attr_setschedpolicy_proc)pContext->posix.pthread_attr_setschedpolicy)(&attr, SCHED_FIFO) == 0) {
6792 scheduler = SCHED_FIFO;
6793 }
6794#endif
6795#ifdef MA_LINUX
6796 } else {
6797 scheduler = sched_getscheduler(0);
6798#endif
6799 }
6800
6801 if (scheduler != -1) {
6802 int priorityMin = sched_get_priority_min(scheduler);
6803 int priorityMax = sched_get_priority_max(scheduler);
6804 int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */
6805
6806 struct sched_param sched;
6807 if (((ma_pthread_attr_getschedparam_proc)pContext->posix.pthread_attr_getschedparam)(&attr, &sched) == 0) {
6808 if (pContext->threadPriority == ma_thread_priority_idle) {
6809 sched.sched_priority = priorityMin;
6810 } else if (pContext->threadPriority == ma_thread_priority_realtime) {
6811 sched.sched_priority = priorityMax;
6812 } else {
6813 sched.sched_priority += ((int)pContext->threadPriority + 5) * priorityStep; /* +5 because the lowest priority is -5. */
6814 if (sched.sched_priority < priorityMin) {
6815 sched.sched_priority = priorityMin;
6816 }
6817 if (sched.sched_priority > priorityMax) {
6818 sched.sched_priority = priorityMax;
6819 }
6820 }
6821
6822 if (((ma_pthread_attr_setschedparam_proc)pContext->posix.pthread_attr_setschedparam)(&attr, &sched) == 0) {
6823 pAttr = &attr;
6824 }
6825 }
6826 }
6827
6828 ((ma_pthread_attr_destroy_proc)pContext->posix.pthread_attr_destroy)(&attr);
6829 }
6830#endif
6831
6832 result = ((ma_pthread_create_proc)pContext->posix.pthread_create)(&pThread->posix.thread, pAttr, entryProc, pData);
6833 if (result != 0) {
6834 return MA_FAILED_TO_CREATE_THREAD;
6835 }
6836
6837 return MA_SUCCESS;
6838}
6839
6840static void ma_thread_wait__posix(ma_thread* pThread)
6841{
6842 ((ma_pthread_join_proc)pThread->pContext->posix.pthread_join)(pThread->posix.thread, NULL);
6843}
6844
6845#if !defined(MA_EMSCRIPTEN)
6846static void ma_sleep__posix(ma_uint32 milliseconds)
6847{
6848#ifdef MA_EMSCRIPTEN
6849 (void)milliseconds;
6850 MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */
6851#else
6852 #if _POSIX_C_SOURCE >= 199309L
6853 struct timespec ts;
6854 ts.tv_sec = milliseconds / 1000000;
6855 ts.tv_nsec = milliseconds % 1000000 * 1000000;
6856 nanosleep(&ts, NULL);
6857 #else
6858 struct timeval tv;
6859 tv.tv_sec = milliseconds / 1000;
6860 tv.tv_usec = milliseconds % 1000 * 1000;
6861 select(0, NULL, NULL, NULL, &tv);
6862 #endif
6863#endif
6864}
6865#endif /* MA_EMSCRIPTEN */
6866
6867
6868static ma_result ma_mutex_init__posix(ma_context* pContext, ma_mutex* pMutex)
6869{
6870 int result = ((ma_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(&pMutex->posix.mutex, NULL);
6871 if (result != 0) {
6872 return MA_FAILED_TO_CREATE_MUTEX;
6873 }
6874
6875 return MA_SUCCESS;
6876}
6877
6878static void ma_mutex_uninit__posix(ma_mutex* pMutex)
6879{
6880 ((ma_pthread_mutex_destroy_proc)pMutex->pContext->posix.pthread_mutex_destroy)(&pMutex->posix.mutex);
6881}
6882
6883static void ma_mutex_lock__posix(ma_mutex* pMutex)
6884{
6885 ((ma_pthread_mutex_lock_proc)pMutex->pContext->posix.pthread_mutex_lock)(&pMutex->posix.mutex);
6886}
6887
6888static void ma_mutex_unlock__posix(ma_mutex* pMutex)
6889{
6890 ((ma_pthread_mutex_unlock_proc)pMutex->pContext->posix.pthread_mutex_unlock)(&pMutex->posix.mutex);
6891}
6892
6893
6894static ma_result ma_event_init__posix(ma_context* pContext, ma_event* pEvent)
6895{
6896 if (((ma_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(&pEvent->posix.mutex, NULL) != 0) {
6897 return MA_FAILED_TO_CREATE_MUTEX;
6898 }
6899
6900 if (((ma_pthread_cond_init_proc)pContext->posix.pthread_cond_init)(&pEvent->posix.condition, NULL) != 0) {
6901 return MA_FAILED_TO_CREATE_EVENT;
6902 }
6903
6904 pEvent->posix.value = 0;
6905 return MA_SUCCESS;
6906}
6907
6908static void ma_event_uninit__posix(ma_event* pEvent)
6909{
6910 ((ma_pthread_cond_destroy_proc)pEvent->pContext->posix.pthread_cond_destroy)(&pEvent->posix.condition);
6911 ((ma_pthread_mutex_destroy_proc)pEvent->pContext->posix.pthread_mutex_destroy)(&pEvent->posix.mutex);
6912}
6913
6914static ma_bool32 ma_event_wait__posix(ma_event* pEvent)
6915{
6916 ((ma_pthread_mutex_lock_proc)pEvent->pContext->posix.pthread_mutex_lock)(&pEvent->posix.mutex);
6917 {
6918 while (pEvent->posix.value == 0) {
6919 ((ma_pthread_cond_wait_proc)pEvent->pContext->posix.pthread_cond_wait)(&pEvent->posix.condition, &pEvent->posix.mutex);
6920 }
6921 pEvent->posix.value = 0; /* Auto-reset. */
6922 }
6923 ((ma_pthread_mutex_unlock_proc)pEvent->pContext->posix.pthread_mutex_unlock)(&pEvent->posix.mutex);
6924
6925 return MA_TRUE;
6926}
6927
6928static ma_bool32 ma_event_signal__posix(ma_event* pEvent)
6929{
6930 ((ma_pthread_mutex_lock_proc)pEvent->pContext->posix.pthread_mutex_lock)(&pEvent->posix.mutex);
6931 {
6932 pEvent->posix.value = 1;
6933 ((ma_pthread_cond_signal_proc)pEvent->pContext->posix.pthread_cond_signal)(&pEvent->posix.condition);
6934 }
6935 ((ma_pthread_mutex_unlock_proc)pEvent->pContext->posix.pthread_mutex_unlock)(&pEvent->posix.mutex);
6936
6937 return MA_TRUE;
6938}
6939
6940
6941static ma_result ma_semaphore_init__posix(ma_context* pContext, int initialValue, ma_semaphore* pSemaphore)
6942{
6943 (void)pContext;
6944
6945#if defined(MA_APPLE)
6946 /* Not yet implemented for Apple platforms since sem_init() is deprecated. Need to use a named semaphore via sem_open() instead. */
6947 return MA_INVALID_OPERATION;
6948#else
6949 if (sem_init(&pSemaphore->posix.semaphore, 0, (unsigned int)initialValue) == 0) {
6950 return MA_FAILED_TO_CREATE_SEMAPHORE;
6951 }
6952#endif
6953
6954 return MA_SUCCESS;
6955}
6956
6957static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore)
6958{
6959 sem_close(&pSemaphore->posix.semaphore);
6960}
6961
6962static ma_bool32 ma_semaphore_wait__posix(ma_semaphore* pSemaphore)
6963{
6964 return sem_wait(&pSemaphore->posix.semaphore) != -1;
6965}
6966
6967static ma_bool32 ma_semaphore_release__posix(ma_semaphore* pSemaphore)
6968{
6969 return sem_post(&pSemaphore->posix.semaphore) != -1;
6970}
6971#endif
6972
6973static ma_result ma_thread_create(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData)
6974{
6975 if (pContext == NULL || pThread == NULL || entryProc == NULL) {
6976 return MA_FALSE;
6977 }
6978
6979 pThread->pContext = pContext;
6980
6981#ifdef MA_WIN32
6982 return ma_thread_create__win32(pContext, pThread, entryProc, pData);
6983#endif
6984#ifdef MA_POSIX
6985 return ma_thread_create__posix(pContext, pThread, entryProc, pData);
6986#endif
6987}
6988
6989static void ma_thread_wait(ma_thread* pThread)
6990{
6991 if (pThread == NULL) {
6992 return;
6993 }
6994
6995#ifdef MA_WIN32
6996 ma_thread_wait__win32(pThread);
6997#endif
6998#ifdef MA_POSIX
6999 ma_thread_wait__posix(pThread);
7000#endif
7001}
7002
7003#if !defined(MA_EMSCRIPTEN)
7004static void ma_sleep(ma_uint32 milliseconds)
7005{
7006#ifdef MA_WIN32
7007 ma_sleep__win32(milliseconds);
7008#endif
7009#ifdef MA_POSIX
7010 ma_sleep__posix(milliseconds);
7011#endif
7012}
7013#endif
7014
7015
7016ma_result ma_mutex_init(ma_context* pContext, ma_mutex* pMutex)
7017{
7018 if (pContext == NULL || pMutex == NULL) {
7019 return MA_INVALID_ARGS;
7020 }
7021
7022 pMutex->pContext = pContext;
7023
7024#ifdef MA_WIN32
7025 return ma_mutex_init__win32(pContext, pMutex);
7026#endif
7027#ifdef MA_POSIX
7028 return ma_mutex_init__posix(pContext, pMutex);
7029#endif
7030}
7031
7032void ma_mutex_uninit(ma_mutex* pMutex)
7033{
7034 if (pMutex == NULL || pMutex->pContext == NULL) {
7035 return;
7036 }
7037
7038#ifdef MA_WIN32
7039 ma_mutex_uninit__win32(pMutex);
7040#endif
7041#ifdef MA_POSIX
7042 ma_mutex_uninit__posix(pMutex);
7043#endif
7044}
7045
7046void ma_mutex_lock(ma_mutex* pMutex)
7047{
7048 if (pMutex == NULL || pMutex->pContext == NULL) {
7049 return;
7050 }
7051
7052#ifdef MA_WIN32
7053 ma_mutex_lock__win32(pMutex);
7054#endif
7055#ifdef MA_POSIX
7056 ma_mutex_lock__posix(pMutex);
7057#endif
7058}
7059
7060void ma_mutex_unlock(ma_mutex* pMutex)
7061{
7062 if (pMutex == NULL || pMutex->pContext == NULL) {
7063 return;
7064}
7065
7066#ifdef MA_WIN32
7067 ma_mutex_unlock__win32(pMutex);
7068#endif
7069#ifdef MA_POSIX
7070 ma_mutex_unlock__posix(pMutex);
7071#endif
7072}
7073
7074
7075ma_result ma_event_init(ma_context* pContext, ma_event* pEvent)
7076{
7077 if (pContext == NULL || pEvent == NULL) {
7078 return MA_FALSE;
7079 }
7080
7081 pEvent->pContext = pContext;
7082
7083#ifdef MA_WIN32
7084 return ma_event_init__win32(pContext, pEvent);
7085#endif
7086#ifdef MA_POSIX
7087 return ma_event_init__posix(pContext, pEvent);
7088#endif
7089}
7090
7091void ma_event_uninit(ma_event* pEvent)
7092{
7093 if (pEvent == NULL || pEvent->pContext == NULL) {
7094 return;
7095 }
7096
7097#ifdef MA_WIN32
7098 ma_event_uninit__win32(pEvent);
7099#endif
7100#ifdef MA_POSIX
7101 ma_event_uninit__posix(pEvent);
7102#endif
7103}
7104
7105ma_bool32 ma_event_wait(ma_event* pEvent)
7106{
7107 if (pEvent == NULL || pEvent->pContext == NULL) {
7108 return MA_FALSE;
7109 }
7110
7111#ifdef MA_WIN32
7112 return ma_event_wait__win32(pEvent);
7113#endif
7114#ifdef MA_POSIX
7115 return ma_event_wait__posix(pEvent);
7116#endif
7117}
7118
7119ma_bool32 ma_event_signal(ma_event* pEvent)
7120{
7121 if (pEvent == NULL || pEvent->pContext == NULL) {
7122 return MA_FALSE;
7123 }
7124
7125#ifdef MA_WIN32
7126 return ma_event_signal__win32(pEvent);
7127#endif
7128#ifdef MA_POSIX
7129 return ma_event_signal__posix(pEvent);
7130#endif
7131}
7132
7133
7134ma_result ma_semaphore_init(ma_context* pContext, int initialValue, ma_semaphore* pSemaphore)
7135{
7136 if (pContext == NULL || pSemaphore == NULL) {
7137 return MA_INVALID_ARGS;
7138 }
7139
7140#ifdef MA_WIN32
7141 return ma_semaphore_init__win32(pContext, initialValue, pSemaphore);
7142#endif
7143#ifdef MA_POSIX
7144 return ma_semaphore_init__posix(pContext, initialValue, pSemaphore);
7145#endif
7146}
7147
7148void ma_semaphore_uninit(ma_semaphore* pSemaphore)
7149{
7150 if (pSemaphore == NULL) {
7151 return;
7152 }
7153
7154#ifdef MA_WIN32
7155 ma_semaphore_uninit__win32(pSemaphore);
7156#endif
7157#ifdef MA_POSIX
7158 ma_semaphore_uninit__posix(pSemaphore);
7159#endif
7160}
7161
7162ma_bool32 ma_semaphore_wait(ma_semaphore* pSemaphore)
7163{
7164 if (pSemaphore == NULL) {
7165 return MA_FALSE;
7166 }
7167
7168#ifdef MA_WIN32
7169 return ma_semaphore_wait__win32(pSemaphore);
7170#endif
7171#ifdef MA_POSIX
7172 return ma_semaphore_wait__posix(pSemaphore);
7173#endif
7174}
7175
7176ma_bool32 ma_semaphore_release(ma_semaphore* pSemaphore)
7177{
7178 if (pSemaphore == NULL) {
7179 return MA_FALSE;
7180 }
7181
7182#ifdef MA_WIN32
7183 return ma_semaphore_release__win32(pSemaphore);
7184#endif
7185#ifdef MA_POSIX
7186 return ma_semaphore_release__posix(pSemaphore);
7187#endif
7188}
7189
7190
7191#if 0
7192ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn)
7193{
7194 ma_uint32 closestRate = 0;
7195 ma_uint32 closestDiff = 0xFFFFFFFF;
7196 size_t iStandardRate;
7197
7198 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
7199 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
7200 ma_uint32 diff;
7201
7202 if (sampleRateIn > standardRate) {
7203 diff = sampleRateIn - standardRate;
7204 } else {
7205 diff = standardRate - sampleRateIn;
7206 }
7207
7208 if (diff == 0) {
7209 return standardRate; /* The input sample rate is a standard rate. */
7210 }
7211
7212 if (closestDiff > diff) {
7213 closestDiff = diff;
7214 closestRate = standardRate;
7215 }
7216 }
7217
7218 return closestRate;
7219}
7220#endif
7221
7222ma_uint32 ma_scale_buffer_size(ma_uint32 baseBufferSize, float scale)
7223{
7224 return ma_max(1, (ma_uint32)(baseBufferSize*scale));
7225}
7226
7227ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate)
7228{
7229 return bufferSizeInFrames / (sampleRate/1000);
7230}
7231
7232ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate)
7233{
7234 return bufferSizeInMilliseconds * (sampleRate/1000);
7235}
7236
7237void ma_zero_pcm_frames(void* p, ma_uint32 frameCount, ma_format format, ma_uint32 channels)
7238{
7239 MA_ZERO_MEMORY(p, frameCount * ma_get_bytes_per_frame(format, channels));
7240}
7241
7242void ma_clip_samples_f32(float* p, ma_uint32 sampleCount)
7243{
7244 ma_uint32 iSample;
7245
7246 /* TODO: Research a branchless SSE implementation. */
7247 for (iSample = 0; iSample < sampleCount; iSample += 1) {
7248 p[iSample] = ma_clip_f32(p[iSample]);
7249 }
7250}
7251
7252
7253void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint32 sampleCount, float factor)
7254{
7255 ma_uint32 iSample;
7256
7257 if (pSamplesOut == NULL || pSamplesIn == NULL) {
7258 return;
7259 }
7260
7261 for (iSample = 0; iSample < sampleCount; iSample += 1) {
7262 pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor);
7263 }
7264}
7265
7266void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint32 sampleCount, float factor)
7267{
7268 ma_uint32 iSample;
7269
7270 if (pSamplesOut == NULL || pSamplesIn == NULL) {
7271 return;
7272 }
7273
7274 for (iSample = 0; iSample < sampleCount; iSample += 1) {
7275 pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor);
7276 }
7277}
7278
7279void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint32 sampleCount, float factor)
7280{
7281 ma_uint32 iSample;
7282 ma_uint8* pSamplesOut8;
7283 ma_uint8* pSamplesIn8;
7284
7285 if (pSamplesOut == NULL || pSamplesIn == NULL) {
7286 return;
7287 }
7288
7289 pSamplesOut8 = (ma_uint8*)pSamplesOut;
7290 pSamplesIn8 = (ma_uint8*)pSamplesIn;
7291
7292 for (iSample = 0; iSample < sampleCount; iSample += 1) {
7293 ma_int32 sampleS32;
7294
7295 sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24);
7296 sampleS32 = (ma_int32)(sampleS32 * factor);
7297
7298 pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8);
7299 pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16);
7300 pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24);
7301 }
7302}
7303
7304void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint32 sampleCount, float factor)
7305{
7306 ma_uint32 iSample;
7307
7308 if (pSamplesOut == NULL || pSamplesIn == NULL) {
7309 return;
7310 }
7311
7312 for (iSample = 0; iSample < sampleCount; iSample += 1) {
7313 pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor);
7314 }
7315}
7316
7317void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint32 sampleCount, float factor)
7318{
7319 ma_uint32 iSample;
7320
7321 if (pSamplesOut == NULL || pSamplesIn == NULL) {
7322 return;
7323 }
7324
7325 for (iSample = 0; iSample < sampleCount; iSample += 1) {
7326 pSamplesOut[iSample] = pSamplesIn[iSample] * factor;
7327 }
7328}
7329
7330void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint32 sampleCount, float factor)
7331{
7332 ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor);
7333}
7334
7335void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint32 sampleCount, float factor)
7336{
7337 ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor);
7338}
7339
7340void ma_apply_volume_factor_s24(void* pSamples, ma_uint32 sampleCount, float factor)
7341{
7342 ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor);
7343}
7344
7345void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint32 sampleCount, float factor)
7346{
7347 ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor);
7348}
7349
7350void ma_apply_volume_factor_f32(float* pSamples, ma_uint32 sampleCount, float factor)
7351{
7352 ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor);
7353}
7354
7355void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
7356{
7357 ma_copy_and_apply_volume_factor_u8(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
7358}
7359
7360void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
7361{
7362 ma_copy_and_apply_volume_factor_s16(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
7363}
7364
7365void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
7366{
7367 ma_copy_and_apply_volume_factor_s24(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
7368}
7369
7370void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
7371{
7372 ma_copy_and_apply_volume_factor_s32(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
7373}
7374
7375void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
7376{
7377 ma_copy_and_apply_volume_factor_f32(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
7378}
7379
7380void ma_copy_and_apply_volume_factor_pcm_frames(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor)
7381{
7382 switch (format)
7383 {
7384 case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pPCMFramesOut, (const ma_uint8*)pPCMFramesIn, frameCount, channels, factor); return;
7385 case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pPCMFramesOut, (const ma_int16*)pPCMFramesIn, frameCount, channels, factor); return;
7386 case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pPCMFramesOut, pPCMFramesIn, frameCount, channels, factor); return;
7387 case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pPCMFramesOut, (const ma_int32*)pPCMFramesIn, frameCount, channels, factor); return;
7388 case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pPCMFramesOut, (const float*)pPCMFramesIn, frameCount, channels, factor); return;
7389 default: return; /* Do nothing. */
7390 }
7391}
7392
7393void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
7394{
7395 ma_copy_and_apply_volume_factor_pcm_frames_u8(pPCMFrames, pPCMFrames, frameCount, channels, factor);
7396}
7397
7398void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
7399{
7400 ma_copy_and_apply_volume_factor_pcm_frames_s16(pPCMFrames, pPCMFrames, frameCount, channels, factor);
7401}
7402
7403void ma_apply_volume_factor_pcm_frames_s24(void* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
7404{
7405 ma_copy_and_apply_volume_factor_pcm_frames_s24(pPCMFrames, pPCMFrames, frameCount, channels, factor);
7406}
7407
7408void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
7409{
7410 ma_copy_and_apply_volume_factor_pcm_frames_s32(pPCMFrames, pPCMFrames, frameCount, channels, factor);
7411}
7412
7413void ma_apply_volume_factor_pcm_frames_f32(float* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
7414{
7415 ma_copy_and_apply_volume_factor_pcm_frames_f32(pPCMFrames, pPCMFrames, frameCount, channels, factor);
7416}
7417
7418void ma_apply_volume_factor_pcm_frames(void* pPCMFrames, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor)
7419{
7420 ma_copy_and_apply_volume_factor_pcm_frames(pPCMFrames, pPCMFrames, frameCount, format, channels, factor);
7421}
7422
7423
7424float ma_factor_to_gain_db(float factor)
7425{
7426 return (float)(20*ma_log10f(factor));
7427}
7428
7429float ma_gain_db_to_factor(float gain)
7430{
7431 return (float)ma_powf(10, gain/20.0f);
7432}
7433
7434
7435static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
7436{
7437 float masterVolumeFactor;
7438
7439 masterVolumeFactor = pDevice->masterVolumeFactor;
7440
7441 if (pDevice->onData) {
7442 if (!pDevice->noPreZeroedOutputBuffer && pFramesOut != NULL) {
7443 ma_zero_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels);
7444 }
7445
7446 /* 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. */
7447 if (pFramesIn != NULL && masterVolumeFactor < 1) {
7448 ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
7449 ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
7450 ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
7451 ma_uint32 totalFramesProcessed = 0;
7452 while (totalFramesProcessed < frameCount) {
7453 ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed;
7454 if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) {
7455 framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture;
7456 }
7457
7458 ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor);
7459
7460 pDevice->onData(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration);
7461
7462 totalFramesProcessed += framesToProcessThisIteration;
7463 }
7464 } else {
7465 pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount);
7466 }
7467
7468 /* Volume control and clipping for playback devices. */
7469 if (pFramesOut != NULL) {
7470 if (masterVolumeFactor < 1) {
7471 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. */
7472 ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor);
7473 }
7474 }
7475
7476 if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) {
7477 ma_clip_pcm_frames_f32((float*)pFramesOut, frameCount, pDevice->playback.channels);
7478 }
7479 }
7480 }
7481}
7482
7483
7484
7485/* A helper function for reading sample data from the client. */
7486static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut)
7487{
7488 MA_ASSERT(pDevice != NULL);
7489 MA_ASSERT(frameCount > 0);
7490 MA_ASSERT(pFramesOut != NULL);
7491
7492 if (pDevice->playback.converter.isPassthrough) {
7493 ma_device__on_data(pDevice, pFramesOut, NULL, frameCount);
7494 } else {
7495 ma_result result;
7496 ma_uint64 totalFramesReadOut;
7497 ma_uint64 totalFramesReadIn;
7498 void* pRunningFramesOut;
7499
7500 totalFramesReadOut = 0;
7501 totalFramesReadIn = 0;
7502 pRunningFramesOut = pFramesOut;
7503
7504 while (totalFramesReadOut < frameCount) {
7505 ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */
7506 ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
7507 ma_uint64 framesToReadThisIterationIn;
7508 ma_uint64 framesReadThisIterationIn;
7509 ma_uint64 framesToReadThisIterationOut;
7510 ma_uint64 framesReadThisIterationOut;
7511 ma_uint64 requiredInputFrameCount;
7512
7513 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
7514 framesToReadThisIterationIn = framesToReadThisIterationOut;
7515 if (framesToReadThisIterationIn > intermediaryBufferCap) {
7516 framesToReadThisIterationIn = intermediaryBufferCap;
7517 }
7518
7519 requiredInputFrameCount = ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, frameCount);
7520 if (framesToReadThisIterationIn > requiredInputFrameCount) {
7521 framesToReadThisIterationIn = requiredInputFrameCount;
7522 }
7523
7524 if (framesToReadThisIterationIn > 0) {
7525 ma_device__on_data(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn);
7526 totalFramesReadIn += framesToReadThisIterationIn;
7527 }
7528
7529 /*
7530 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
7531 input frames, we still want to try processing frames because there may some output frames generated from cached input data.
7532 */
7533 framesReadThisIterationIn = framesToReadThisIterationIn;
7534 framesReadThisIterationOut = framesToReadThisIterationOut;
7535 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
7536 if (result != MA_SUCCESS) {
7537 break;
7538 }
7539
7540 totalFramesReadOut += framesReadThisIterationOut;
7541 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
7542
7543 if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
7544 break; /* We're done. */
7545 }
7546 }
7547 }
7548}
7549
7550/* A helper for sending sample data to the client. */
7551static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat)
7552{
7553 MA_ASSERT(pDevice != NULL);
7554 MA_ASSERT(frameCountInDeviceFormat > 0);
7555 MA_ASSERT(pFramesInDeviceFormat != NULL);
7556
7557 if (pDevice->capture.converter.isPassthrough) {
7558 ma_device__on_data(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat);
7559 } else {
7560 ma_result result;
7561 ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
7562 ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
7563 ma_uint64 totalDeviceFramesProcessed = 0;
7564 ma_uint64 totalClientFramesProcessed = 0;
7565 const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;
7566
7567 /* We just keep going until we've exhaused all of our input frames and cannot generate any more output frames. */
7568 for (;;) {
7569 ma_uint64 deviceFramesProcessedThisIteration;
7570 ma_uint64 clientFramesProcessedThisIteration;
7571
7572 deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
7573 clientFramesProcessedThisIteration = framesInClientFormatCap;
7574
7575 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration);
7576 if (result != MA_SUCCESS) {
7577 break;
7578 }
7579
7580 if (clientFramesProcessedThisIteration > 0) {
7581 ma_device__on_data(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */
7582 }
7583
7584 pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
7585 totalDeviceFramesProcessed += deviceFramesProcessedThisIteration;
7586 totalClientFramesProcessed += clientFramesProcessedThisIteration;
7587
7588 if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) {
7589 break; /* We're done. */
7590 }
7591 }
7592 }
7593}
7594
7595
7596/* We only want to expose ma_device__handle_duplex_callback_capture() and ma_device__handle_duplex_callback_playback() if we have an asynchronous backend enabled. */
7597#if defined(MA_HAS_JACK) || \
7598 defined(MA_HAS_COREAUDIO) || \
7599 defined(MA_HAS_AAUDIO) || \
7600 defined(MA_HAS_OPENSL) || \
7601 defined(MA_HAS_WEBAUDIO)
7602static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB)
7603{
7604 ma_result result;
7605 ma_uint32 totalDeviceFramesProcessed = 0;
7606 const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;
7607
7608 MA_ASSERT(pDevice != NULL);
7609 MA_ASSERT(frameCountInDeviceFormat > 0);
7610 MA_ASSERT(pFramesInDeviceFormat != NULL);
7611 MA_ASSERT(pRB != NULL);
7612
7613 /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */
7614 for (;;) {
7615 ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
7616 ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
7617 ma_uint64 framesProcessedInDeviceFormat;
7618 ma_uint64 framesProcessedInClientFormat;
7619 void* pFramesInClientFormat;
7620
7621 result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat);
7622 if (result != MA_SUCCESS) {
7623 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer.", result);
7624 break;
7625 }
7626
7627 if (framesToProcessInClientFormat == 0) {
7628 if (ma_pcm_rb_pointer_disance(pRB) == (ma_int32)ma_pcm_rb_get_subbuffer_size(pRB)) {
7629 break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */
7630 }
7631 }
7632
7633 /* Convert. */
7634 framesProcessedInDeviceFormat = framesToProcessInDeviceFormat;
7635 framesProcessedInClientFormat = framesToProcessInClientFormat;
7636 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat);
7637 if (result != MA_SUCCESS) {
7638 break;
7639 }
7640
7641 result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInDeviceFormat, pFramesInClientFormat); /* Safe cast. */
7642 if (result != MA_SUCCESS) {
7643 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer.", result);
7644 break;
7645 }
7646
7647 pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
7648 totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */
7649
7650 /* We're done when we're unable to process any client nor device frames. */
7651 if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) {
7652 break; /* Done. */
7653 }
7654 }
7655
7656 return MA_SUCCESS;
7657}
7658
7659static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB)
7660{
7661 ma_result result;
7662 ma_uint8 playbackFramesInExternalFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
7663 ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
7664 ma_uint32 totalFramesToReadFromClient;
7665 ma_uint32 totalFramesReadFromClient;
7666 ma_uint32 totalFramesReadOut = 0;
7667
7668 MA_ASSERT(pDevice != NULL);
7669 MA_ASSERT(frameCount > 0);
7670 MA_ASSERT(pFramesInInternalFormat != NULL);
7671 MA_ASSERT(pRB != NULL);
7672
7673 /*
7674 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
7675 the whole frameCount frames we just use silence instead for the input data.
7676 */
7677 MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames));
7678
7679 /* We need to calculate how many output frames are required to be read from the client to completely fill frameCount internal frames. */
7680 totalFramesToReadFromClient = (ma_uint32)ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, frameCount);
7681 totalFramesReadFromClient = 0;
7682 while (totalFramesReadFromClient < totalFramesToReadFromClient && ma_device_is_started(pDevice)) {
7683 ma_uint32 framesRemainingFromClient;
7684 ma_uint32 framesToProcessFromClient;
7685 ma_uint32 inputFrameCount;
7686 void* pInputFrames;
7687
7688 framesRemainingFromClient = (totalFramesToReadFromClient - totalFramesReadFromClient);
7689 framesToProcessFromClient = sizeof(playbackFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
7690 if (framesToProcessFromClient > framesRemainingFromClient) {
7691 framesToProcessFromClient = framesRemainingFromClient;
7692 }
7693
7694 /* We need to grab captured samples before firing the callback. If there's not enough input samples we just pass silence. */
7695 inputFrameCount = framesToProcessFromClient;
7696 result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames);
7697 if (result == MA_SUCCESS) {
7698 if (inputFrameCount > 0) {
7699 /* Use actual input frames. */
7700 ma_device__on_data(pDevice, playbackFramesInExternalFormat, pInputFrames, inputFrameCount);
7701 } else {
7702 if (ma_pcm_rb_pointer_disance(pRB) == 0) {
7703 break; /* Underrun. */
7704 }
7705 }
7706
7707 /* We're done with the captured samples. */
7708 result = ma_pcm_rb_commit_read(pRB, inputFrameCount, pInputFrames);
7709 if (result != MA_SUCCESS) {
7710 break; /* Don't know what to do here... Just abandon ship. */
7711 }
7712 } else {
7713 /* Use silent input frames. */
7714 inputFrameCount = ma_min(
7715 sizeof(playbackFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels),
7716 sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)
7717 );
7718
7719 ma_device__on_data(pDevice, playbackFramesInExternalFormat, silentInputFrames, inputFrameCount);
7720 }
7721
7722 /* We have samples in external format so now we need to convert to internal format and output to the device. */
7723 {
7724 ma_uint64 framesConvertedIn = inputFrameCount;
7725 ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut);
7726 ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackFramesInExternalFormat, &framesConvertedIn, pFramesInInternalFormat, &framesConvertedOut);
7727
7728 totalFramesReadFromClient += (ma_uint32)framesConvertedIn; /* Safe cast. */
7729 totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */
7730 pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
7731 }
7732 }
7733
7734 return MA_SUCCESS;
7735}
7736#endif /* Asynchronous backends. */
7737
7738/* A helper for changing the state of the device. */
7739static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_uint32 newState)
7740{
7741 ma_atomic_exchange_32(&pDevice->state, newState);
7742}
7743
7744/* A helper for getting the state of the device. */
7745static MA_INLINE ma_uint32 ma_device__get_state(ma_device* pDevice)
7746{
7747 return pDevice->state;
7748}
7749
7750
7751#ifdef MA_WIN32
7752 GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
7753 GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
7754 /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
7755 /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
7756#endif
7757
7758
7759typedef struct
7760{
7761 ma_device_type deviceType;
7762 const ma_device_id* pDeviceID;
7763 char* pName;
7764 size_t nameBufferSize;
7765 ma_bool32 foundDevice;
7766} ma_context__try_get_device_name_by_id__enum_callback_data;
7767
7768static ma_bool32 ma_context__try_get_device_name_by_id__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData)
7769{
7770 ma_context__try_get_device_name_by_id__enum_callback_data* pData = (ma_context__try_get_device_name_by_id__enum_callback_data*)pUserData;
7771 MA_ASSERT(pData != NULL);
7772
7773 if (pData->deviceType == deviceType) {
7774 if (pContext->onDeviceIDEqual(pContext, pData->pDeviceID, &pDeviceInfo->id)) {
7775 ma_strncpy_s(pData->pName, pData->nameBufferSize, pDeviceInfo->name, (size_t)-1);
7776 pData->foundDevice = MA_TRUE;
7777 }
7778 }
7779
7780 return !pData->foundDevice;
7781}
7782
7783/*
7784Generic function for retrieving the name of a device by it's ID.
7785
7786This function simply enumerates every device and then retrieves the name of the first device that has the same ID.
7787*/
7788static ma_result ma_context__try_get_device_name_by_id(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, char* pName, size_t nameBufferSize)
7789{
7790 ma_result result;
7791 ma_context__try_get_device_name_by_id__enum_callback_data data;
7792
7793 MA_ASSERT(pContext != NULL);
7794 MA_ASSERT(pName != NULL);
7795
7796 if (pDeviceID == NULL) {
7797 return MA_NO_DEVICE;
7798 }
7799
7800 data.deviceType = deviceType;
7801 data.pDeviceID = pDeviceID;
7802 data.pName = pName;
7803 data.nameBufferSize = nameBufferSize;
7804 data.foundDevice = MA_FALSE;
7805 result = ma_context_enumerate_devices(pContext, ma_context__try_get_device_name_by_id__enum_callback, &data);
7806 if (result != MA_SUCCESS) {
7807 return result;
7808 }
7809
7810 if (!data.foundDevice) {
7811 return MA_NO_DEVICE;
7812 } else {
7813 return MA_SUCCESS;
7814 }
7815}
7816
7817
7818ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */
7819{
7820 ma_uint32 i;
7821 for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) {
7822 if (g_maFormatPriorities[i] == format) {
7823 return i;
7824 }
7825 }
7826
7827 /* Getting here means the format could not be found or is equal to ma_format_unknown. */
7828 return (ma_uint32)-1;
7829}
7830
7831static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType);
7832
7833
7834/*******************************************************************************
7835
7836Null Backend
7837
7838*******************************************************************************/
7839#ifdef MA_HAS_NULL
7840
7841#define MA_DEVICE_OP_NONE__NULL 0
7842#define MA_DEVICE_OP_START__NULL 1
7843#define MA_DEVICE_OP_SUSPEND__NULL 2
7844#define MA_DEVICE_OP_KILL__NULL 3
7845
7846static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData)
7847{
7848 ma_device* pDevice = (ma_device*)pData;
7849 MA_ASSERT(pDevice != NULL);
7850
7851 for (;;) { /* Keep the thread alive until the device is uninitialized. */
7852 /* Wait for an operation to be requested. */
7853 ma_event_wait(&pDevice->null_device.operationEvent);
7854
7855 /* At this point an event should have been triggered. */
7856
7857 /* Starting the device needs to put the thread into a loop. */
7858 if (pDevice->null_device.operation == MA_DEVICE_OP_START__NULL) {
7859 ma_atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);
7860
7861 /* Reset the timer just in case. */
7862 ma_timer_init(&pDevice->null_device.timer);
7863
7864 /* Keep looping until an operation has been requested. */
7865 while (pDevice->null_device.operation != MA_DEVICE_OP_NONE__NULL && pDevice->null_device.operation != MA_DEVICE_OP_START__NULL) {
7866 ma_sleep(10); /* Don't hog the CPU. */
7867 }
7868
7869 /* Getting here means a suspend or kill operation has been requested. */
7870 ma_atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS);
7871 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
7872 continue;
7873 }
7874
7875 /* Suspending the device means we need to stop the timer and just continue the loop. */
7876 if (pDevice->null_device.operation == MA_DEVICE_OP_SUSPEND__NULL) {
7877 ma_atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);
7878
7879 /* We need to add the current run time to the prior run time, then reset the timer. */
7880 pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer);
7881 ma_timer_init(&pDevice->null_device.timer);
7882
7883 /* We're done. */
7884 ma_atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS);
7885 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
7886 continue;
7887 }
7888
7889 /* Killing the device means we need to get out of this loop so that this thread can terminate. */
7890 if (pDevice->null_device.operation == MA_DEVICE_OP_KILL__NULL) {
7891 ma_atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);
7892 ma_atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS);
7893 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
7894 break;
7895 }
7896
7897 /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */
7898 if (pDevice->null_device.operation == MA_DEVICE_OP_NONE__NULL) {
7899 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). */
7900 ma_atomic_exchange_32(&pDevice->null_device.operationResult, MA_INVALID_OPERATION);
7901 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
7902 continue; /* Continue the loop. Don't terminate. */
7903 }
7904 }
7905
7906 return (ma_thread_result)0;
7907}
7908
7909static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation)
7910{
7911 ma_atomic_exchange_32(&pDevice->null_device.operation, operation);
7912 if (!ma_event_signal(&pDevice->null_device.operationEvent)) {
7913 return MA_ERROR;
7914 }
7915
7916 if (!ma_event_wait(&pDevice->null_device.operationCompletionEvent)) {
7917 return MA_ERROR;
7918 }
7919
7920 return pDevice->null_device.operationResult;
7921}
7922
7923static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice)
7924{
7925 ma_uint32 internalSampleRate;
7926 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
7927 internalSampleRate = pDevice->capture.internalSampleRate;
7928 } else {
7929 internalSampleRate = pDevice->playback.internalSampleRate;
7930 }
7931
7932
7933 return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate);
7934}
7935
7936static ma_bool32 ma_context_is_device_id_equal__null(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
7937{
7938 MA_ASSERT(pContext != NULL);
7939 MA_ASSERT(pID0 != NULL);
7940 MA_ASSERT(pID1 != NULL);
7941 (void)pContext;
7942
7943 return pID0->nullbackend == pID1->nullbackend;
7944}
7945
7946static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
7947{
7948 ma_bool32 cbResult = MA_TRUE;
7949
7950 MA_ASSERT(pContext != NULL);
7951 MA_ASSERT(callback != NULL);
7952
7953 /* Playback. */
7954 if (cbResult) {
7955 ma_device_info deviceInfo;
7956 MA_ZERO_OBJECT(&deviceInfo);
7957 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1);
7958 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
7959 }
7960
7961 /* Capture. */
7962 if (cbResult) {
7963 ma_device_info deviceInfo;
7964 MA_ZERO_OBJECT(&deviceInfo);
7965 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1);
7966 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
7967 }
7968
7969 return MA_SUCCESS;
7970}
7971
7972static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
7973{
7974 ma_uint32 iFormat;
7975
7976 MA_ASSERT(pContext != NULL);
7977
7978 if (pDeviceID != NULL && pDeviceID->nullbackend != 0) {
7979 return MA_NO_DEVICE; /* Don't know the device. */
7980 }
7981
7982 /* Name / Description */
7983 if (deviceType == ma_device_type_playback) {
7984 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1);
7985 } else {
7986 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1);
7987 }
7988
7989 /* Support everything on the null backend. */
7990 pDeviceInfo->formatCount = ma_format_count - 1; /* Minus one because we don't want to include ma_format_unknown. */
7991 for (iFormat = 0; iFormat < pDeviceInfo->formatCount; ++iFormat) {
7992 pDeviceInfo->formats[iFormat] = (ma_format)(iFormat + 1); /* +1 to skip over ma_format_unknown. */
7993 }
7994
7995 pDeviceInfo->minChannels = 1;
7996 pDeviceInfo->maxChannels = MA_MAX_CHANNELS;
7997 pDeviceInfo->minSampleRate = MA_SAMPLE_RATE_8000;
7998 pDeviceInfo->maxSampleRate = MA_SAMPLE_RATE_384000;
7999
8000 (void)pContext;
8001 (void)shareMode;
8002 return MA_SUCCESS;
8003}
8004
8005
8006static void ma_device_uninit__null(ma_device* pDevice)
8007{
8008 MA_ASSERT(pDevice != NULL);
8009
8010 /* Keep it clean and wait for the device thread to finish before returning. */
8011 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL);
8012
8013 /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */
8014 ma_event_uninit(&pDevice->null_device.operationCompletionEvent);
8015 ma_event_uninit(&pDevice->null_device.operationEvent);
8016}
8017
8018static ma_result ma_device_init__null(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
8019{
8020 ma_result result;
8021 ma_uint32 periodSizeInFrames;
8022
8023 MA_ASSERT(pDevice != NULL);
8024
8025 MA_ZERO_OBJECT(&pDevice->null_device);
8026
8027 if (pConfig->deviceType == ma_device_type_loopback) {
8028 return MA_DEVICE_TYPE_NOT_SUPPORTED;
8029 }
8030
8031 periodSizeInFrames = pConfig->periodSizeInFrames;
8032 if (periodSizeInFrames == 0) {
8033 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pConfig->sampleRate);
8034 }
8035
8036 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
8037 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "NULL Capture Device", (size_t)-1);
8038 pDevice->capture.internalFormat = pConfig->capture.format;
8039 pDevice->capture.internalChannels = pConfig->capture.channels;
8040 ma_channel_map_copy(pDevice->capture.internalChannelMap, pConfig->capture.channelMap, pConfig->capture.channels);
8041 pDevice->capture.internalPeriodSizeInFrames = periodSizeInFrames;
8042 pDevice->capture.internalPeriods = pConfig->periods;
8043 }
8044 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
8045 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "NULL Playback Device", (size_t)-1);
8046 pDevice->playback.internalFormat = pConfig->playback.format;
8047 pDevice->playback.internalChannels = pConfig->playback.channels;
8048 ma_channel_map_copy(pDevice->playback.internalChannelMap, pConfig->playback.channelMap, pConfig->playback.channels);
8049 pDevice->playback.internalPeriodSizeInFrames = periodSizeInFrames;
8050 pDevice->playback.internalPeriods = pConfig->periods;
8051 }
8052
8053 /*
8054 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
8055 first period is "written" to it, and then stopped in ma_device_stop__null().
8056 */
8057 result = ma_event_init(pContext, &pDevice->null_device.operationEvent);
8058 if (result != MA_SUCCESS) {
8059 return result;
8060 }
8061
8062 result = ma_event_init(pContext, &pDevice->null_device.operationCompletionEvent);
8063 if (result != MA_SUCCESS) {
8064 return result;
8065 }
8066
8067 result = ma_thread_create(pContext, &pDevice->thread, ma_device_thread__null, pDevice);
8068 if (result != MA_SUCCESS) {
8069 return result;
8070 }
8071
8072 return MA_SUCCESS;
8073}
8074
8075static ma_result ma_device_start__null(ma_device* pDevice)
8076{
8077 MA_ASSERT(pDevice != NULL);
8078
8079 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL);
8080
8081 ma_atomic_exchange_32(&pDevice->null_device.isStarted, MA_TRUE);
8082 return MA_SUCCESS;
8083}
8084
8085static ma_result ma_device_stop__null(ma_device* pDevice)
8086{
8087 MA_ASSERT(pDevice != NULL);
8088
8089 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL);
8090
8091 ma_atomic_exchange_32(&pDevice->null_device.isStarted, MA_FALSE);
8092 return MA_SUCCESS;
8093}
8094
8095static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
8096{
8097 ma_result result = MA_SUCCESS;
8098 ma_uint32 totalPCMFramesProcessed;
8099 ma_bool32 wasStartedOnEntry;
8100
8101 if (pFramesWritten != NULL) {
8102 *pFramesWritten = 0;
8103 }
8104
8105 wasStartedOnEntry = pDevice->null_device.isStarted;
8106
8107 /* Keep going until everything has been read. */
8108 totalPCMFramesProcessed = 0;
8109 while (totalPCMFramesProcessed < frameCount) {
8110 ma_uint64 targetFrame;
8111
8112 /* If there are any frames remaining in the current period, consume those first. */
8113 if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) {
8114 ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
8115 ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback;
8116 if (framesToProcess > framesRemaining) {
8117 framesToProcess = framesRemaining;
8118 }
8119
8120 /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */
8121 (void)pPCMFrames;
8122
8123 pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess;
8124 totalPCMFramesProcessed += framesToProcess;
8125 }
8126
8127 /* 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. */
8128 if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) {
8129 pDevice->null_device.currentPeriodFramesRemainingPlayback = 0;
8130
8131 if (!pDevice->null_device.isStarted && !wasStartedOnEntry) {
8132 result = ma_device_start__null(pDevice);
8133 if (result != MA_SUCCESS) {
8134 break;
8135 }
8136 }
8137 }
8138
8139 /* If we've consumed the whole buffer we can return now. */
8140 MA_ASSERT(totalPCMFramesProcessed <= frameCount);
8141 if (totalPCMFramesProcessed == frameCount) {
8142 break;
8143 }
8144
8145 /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
8146 targetFrame = pDevice->null_device.lastProcessedFramePlayback;
8147 for (;;) {
8148 ma_uint64 currentFrame;
8149
8150 /* Stop waiting if the device has been stopped. */
8151 if (!pDevice->null_device.isStarted) {
8152 break;
8153 }
8154
8155 currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
8156 if (currentFrame >= targetFrame) {
8157 break;
8158 }
8159
8160 /* Getting here means we haven't yet reached the target sample, so continue waiting. */
8161 ma_sleep(10);
8162 }
8163
8164 pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalPeriodSizeInFrames;
8165 pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalPeriodSizeInFrames;
8166 }
8167
8168 if (pFramesWritten != NULL) {
8169 *pFramesWritten = totalPCMFramesProcessed;
8170 }
8171
8172 return result;
8173}
8174
8175static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
8176{
8177 ma_result result = MA_SUCCESS;
8178 ma_uint32 totalPCMFramesProcessed;
8179
8180 if (pFramesRead != NULL) {
8181 *pFramesRead = 0;
8182 }
8183
8184 /* Keep going until everything has been read. */
8185 totalPCMFramesProcessed = 0;
8186 while (totalPCMFramesProcessed < frameCount) {
8187 ma_uint64 targetFrame;
8188
8189 /* If there are any frames remaining in the current period, consume those first. */
8190 if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) {
8191 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
8192 ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
8193 ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture;
8194 if (framesToProcess > framesRemaining) {
8195 framesToProcess = framesRemaining;
8196 }
8197
8198 /* We need to ensured the output buffer is zeroed. */
8199 MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf);
8200
8201 pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess;
8202 totalPCMFramesProcessed += framesToProcess;
8203 }
8204
8205 /* 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. */
8206 if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) {
8207 pDevice->null_device.currentPeriodFramesRemainingCapture = 0;
8208 }
8209
8210 /* If we've consumed the whole buffer we can return now. */
8211 MA_ASSERT(totalPCMFramesProcessed <= frameCount);
8212 if (totalPCMFramesProcessed == frameCount) {
8213 break;
8214 }
8215
8216 /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
8217 targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames;
8218 for (;;) {
8219 ma_uint64 currentFrame;
8220
8221 /* Stop waiting if the device has been stopped. */
8222 if (!pDevice->null_device.isStarted) {
8223 break;
8224 }
8225
8226 currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
8227 if (currentFrame >= targetFrame) {
8228 break;
8229 }
8230
8231 /* Getting here means we haven't yet reached the target sample, so continue waiting. */
8232 ma_sleep(10);
8233 }
8234
8235 pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalPeriodSizeInFrames;
8236 pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames;
8237 }
8238
8239 if (pFramesRead != NULL) {
8240 *pFramesRead = totalPCMFramesProcessed;
8241 }
8242
8243 return result;
8244}
8245
8246static ma_result ma_device_main_loop__null(ma_device* pDevice)
8247{
8248 ma_result result = MA_SUCCESS;
8249 ma_bool32 exitLoop = MA_FALSE;
8250
8251 MA_ASSERT(pDevice != NULL);
8252
8253 /* The capture device needs to be started immediately. */
8254 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
8255 result = ma_device_start__null(pDevice);
8256 if (result != MA_SUCCESS) {
8257 return result;
8258 }
8259 }
8260
8261 while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
8262 switch (pDevice->type)
8263 {
8264 case ma_device_type_duplex:
8265 {
8266 /* The process is: device_read -> convert -> callback -> convert -> device_write */
8267 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
8268 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
8269
8270 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
8271 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
8272 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
8273 ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
8274 ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
8275 ma_uint32 capturedDeviceFramesRemaining;
8276 ma_uint32 capturedDeviceFramesProcessed;
8277 ma_uint32 capturedDeviceFramesToProcess;
8278 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
8279 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
8280 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
8281 }
8282
8283 result = ma_device_read__null(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
8284 if (result != MA_SUCCESS) {
8285 exitLoop = MA_TRUE;
8286 break;
8287 }
8288
8289 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
8290 capturedDeviceFramesProcessed = 0;
8291
8292 /* At this point we have our captured data in device format and we now need to convert it to client format. */
8293 for (;;) {
8294 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
8295 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
8296 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
8297 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
8298 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
8299 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
8300 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
8301
8302 /* Convert capture data from device format to client format. */
8303 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
8304 if (result != MA_SUCCESS) {
8305 break;
8306 }
8307
8308 /*
8309 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
8310 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
8311 */
8312 if (capturedClientFramesToProcessThisIteration == 0) {
8313 break;
8314 }
8315
8316 ma_device__on_data(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
8317
8318 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
8319 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
8320
8321 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
8322 for (;;) {
8323 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
8324 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
8325 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
8326 if (result != MA_SUCCESS) {
8327 break;
8328 }
8329
8330 result = ma_device_write__null(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
8331 if (result != MA_SUCCESS) {
8332 exitLoop = MA_TRUE;
8333 break;
8334 }
8335
8336 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
8337 if (capturedClientFramesToProcessThisIteration == 0) {
8338 break;
8339 }
8340 }
8341
8342 /* In case an error happened from ma_device_write__null()... */
8343 if (result != MA_SUCCESS) {
8344 exitLoop = MA_TRUE;
8345 break;
8346 }
8347 }
8348
8349 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
8350 }
8351 } break;
8352
8353 case ma_device_type_capture:
8354 {
8355 /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
8356 ma_uint8 intermediaryBuffer[8192];
8357 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
8358 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
8359 ma_uint32 framesReadThisPeriod = 0;
8360 while (framesReadThisPeriod < periodSizeInFrames) {
8361 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
8362 ma_uint32 framesProcessed;
8363 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
8364 if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
8365 framesToReadThisIteration = intermediaryBufferSizeInFrames;
8366 }
8367
8368 result = ma_device_read__null(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
8369 if (result != MA_SUCCESS) {
8370 exitLoop = MA_TRUE;
8371 break;
8372 }
8373
8374 ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
8375
8376 framesReadThisPeriod += framesProcessed;
8377 }
8378 } break;
8379
8380 case ma_device_type_playback:
8381 {
8382 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
8383 ma_uint8 intermediaryBuffer[8192];
8384 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
8385 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
8386 ma_uint32 framesWrittenThisPeriod = 0;
8387 while (framesWrittenThisPeriod < periodSizeInFrames) {
8388 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
8389 ma_uint32 framesProcessed;
8390 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
8391 if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
8392 framesToWriteThisIteration = intermediaryBufferSizeInFrames;
8393 }
8394
8395 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
8396
8397 result = ma_device_write__null(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
8398 if (result != MA_SUCCESS) {
8399 exitLoop = MA_TRUE;
8400 break;
8401 }
8402
8403 framesWrittenThisPeriod += framesProcessed;
8404 }
8405 } break;
8406
8407 /* To silence a warning. Will never hit this. */
8408 case ma_device_type_loopback:
8409 default: break;
8410 }
8411 }
8412
8413
8414 /* Here is where the device is started. */
8415 ma_device_stop__null(pDevice);
8416
8417 return result;
8418}
8419
8420static ma_result ma_context_uninit__null(ma_context* pContext)
8421{
8422 MA_ASSERT(pContext != NULL);
8423 MA_ASSERT(pContext->backend == ma_backend_null);
8424
8425 (void)pContext;
8426 return MA_SUCCESS;
8427}
8428
8429static ma_result ma_context_init__null(const ma_context_config* pConfig, ma_context* pContext)
8430{
8431 MA_ASSERT(pContext != NULL);
8432
8433 (void)pConfig;
8434
8435 pContext->onUninit = ma_context_uninit__null;
8436 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__null;
8437 pContext->onEnumDevices = ma_context_enumerate_devices__null;
8438 pContext->onGetDeviceInfo = ma_context_get_device_info__null;
8439 pContext->onDeviceInit = ma_device_init__null;
8440 pContext->onDeviceUninit = ma_device_uninit__null;
8441 pContext->onDeviceStart = NULL; /* Not required for synchronous backends. */
8442 pContext->onDeviceStop = NULL; /* Not required for synchronous backends. */
8443 pContext->onDeviceMainLoop = ma_device_main_loop__null;
8444
8445 /* The null backend always works. */
8446 return MA_SUCCESS;
8447}
8448#endif
8449
8450
8451/*******************************************************************************
8452
8453WIN32 COMMON
8454
8455*******************************************************************************/
8456#if defined(MA_WIN32)
8457#if defined(MA_WIN32_DESKTOP)
8458 #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit)
8459 #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)()
8460 #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv)
8461 #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv)
8462 #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar)
8463#else
8464 #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit)
8465 #define ma_CoUninitialize(pContext) CoUninitialize()
8466 #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv)
8467 #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv)
8468 #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar)
8469#endif
8470
8471#if !defined(MAXULONG_PTR)
8472typedef size_t DWORD_PTR;
8473#endif
8474
8475#if !defined(WAVE_FORMAT_44M08)
8476#define WAVE_FORMAT_44M08 0x00000100
8477#define WAVE_FORMAT_44S08 0x00000200
8478#define WAVE_FORMAT_44M16 0x00000400
8479#define WAVE_FORMAT_44S16 0x00000800
8480#define WAVE_FORMAT_48M08 0x00001000
8481#define WAVE_FORMAT_48S08 0x00002000
8482#define WAVE_FORMAT_48M16 0x00004000
8483#define WAVE_FORMAT_48S16 0x00008000
8484#define WAVE_FORMAT_96M08 0x00010000
8485#define WAVE_FORMAT_96S08 0x00020000
8486#define WAVE_FORMAT_96M16 0x00040000
8487#define WAVE_FORMAT_96S16 0x00080000
8488#endif
8489
8490#ifndef SPEAKER_FRONT_LEFT
8491#define SPEAKER_FRONT_LEFT 0x1
8492#define SPEAKER_FRONT_RIGHT 0x2
8493#define SPEAKER_FRONT_CENTER 0x4
8494#define SPEAKER_LOW_FREQUENCY 0x8
8495#define SPEAKER_BACK_LEFT 0x10
8496#define SPEAKER_BACK_RIGHT 0x20
8497#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
8498#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
8499#define SPEAKER_BACK_CENTER 0x100
8500#define SPEAKER_SIDE_LEFT 0x200
8501#define SPEAKER_SIDE_RIGHT 0x400
8502#define SPEAKER_TOP_CENTER 0x800
8503#define SPEAKER_TOP_FRONT_LEFT 0x1000
8504#define SPEAKER_TOP_FRONT_CENTER 0x2000
8505#define SPEAKER_TOP_FRONT_RIGHT 0x4000
8506#define SPEAKER_TOP_BACK_LEFT 0x8000
8507#define SPEAKER_TOP_BACK_CENTER 0x10000
8508#define SPEAKER_TOP_BACK_RIGHT 0x20000
8509#endif
8510
8511/*
8512The SDK that comes with old versions of MSVC (VC6, for example) does not appear to define WAVEFORMATEXTENSIBLE. We
8513define our own implementation in this case.
8514*/
8515#if (defined(_MSC_VER) && !defined(_WAVEFORMATEXTENSIBLE_)) || defined(__DMC__)
8516typedef struct
8517{
8518 WAVEFORMATEX Format;
8519 union
8520 {
8521 WORD wValidBitsPerSample;
8522 WORD wSamplesPerBlock;
8523 WORD wReserved;
8524 } Samples;
8525 DWORD dwChannelMask;
8526 GUID SubFormat;
8527} WAVEFORMATEXTENSIBLE;
8528#endif
8529
8530#ifndef WAVE_FORMAT_EXTENSIBLE
8531#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
8532#endif
8533
8534#ifndef WAVE_FORMAT_IEEE_FLOAT
8535#define WAVE_FORMAT_IEEE_FLOAT 0x0003
8536#endif
8537
8538static GUID MA_GUID_NULL = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
8539
8540/* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
8541static ma_uint8 ma_channel_id_to_ma__win32(DWORD id)
8542{
8543 switch (id)
8544 {
8545 case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
8546 case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
8547 case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
8548 case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
8549 case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
8550 case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
8551 case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
8552 case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
8553 case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
8554 case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
8555 case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
8556 case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
8557 case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
8558 case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
8559 case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
8560 case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
8561 case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
8562 case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
8563 default: return 0;
8564 }
8565}
8566
8567/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */
8568static DWORD ma_channel_id_to_win32(DWORD id)
8569{
8570 switch (id)
8571 {
8572 case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER;
8573 case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT;
8574 case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT;
8575 case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER;
8576 case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY;
8577 case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT;
8578 case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT;
8579 case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER;
8580 case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER;
8581 case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER;
8582 case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT;
8583 case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT;
8584 case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER;
8585 case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT;
8586 case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER;
8587 case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT;
8588 case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT;
8589 case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER;
8590 case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT;
8591 default: return 0;
8592 }
8593}
8594
8595/* Converts a channel mapping to a Win32-style channel mask. */
8596static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel channelMap[MA_MAX_CHANNELS], ma_uint32 channels)
8597{
8598 DWORD dwChannelMask = 0;
8599 ma_uint32 iChannel;
8600
8601 for (iChannel = 0; iChannel < channels; ++iChannel) {
8602 dwChannelMask |= ma_channel_id_to_win32(channelMap[iChannel]);
8603 }
8604
8605 return dwChannelMask;
8606}
8607
8608/* Converts a Win32-style channel mask to a miniaudio channel map. */
8609static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
8610{
8611 if (channels == 1 && dwChannelMask == 0) {
8612 channelMap[0] = MA_CHANNEL_MONO;
8613 } else if (channels == 2 && dwChannelMask == 0) {
8614 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
8615 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
8616 } else {
8617 if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) {
8618 channelMap[0] = MA_CHANNEL_MONO;
8619 } else {
8620 /* Just iterate over each bit. */
8621 ma_uint32 iChannel = 0;
8622 ma_uint32 iBit;
8623
8624 for (iBit = 0; iBit < 32; ++iBit) {
8625 DWORD bitValue = (dwChannelMask & (1UL << iBit));
8626 if (bitValue != 0) {
8627 /* The bit is set. */
8628 channelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue);
8629 iChannel += 1;
8630 }
8631 }
8632 }
8633 }
8634}
8635
8636#ifdef __cplusplus
8637static ma_bool32 ma_is_guid_equal(const void* a, const void* b)
8638{
8639 return IsEqualGUID(*(const GUID*)a, *(const GUID*)b);
8640}
8641#else
8642#define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b)
8643#endif
8644
8645static ma_format ma_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF)
8646{
8647 MA_ASSERT(pWF != NULL);
8648
8649 if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
8650 const WAVEFORMATEXTENSIBLE* pWFEX = (const WAVEFORMATEXTENSIBLE*)pWF;
8651 if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) {
8652 if (pWFEX->Samples.wValidBitsPerSample == 32) {
8653 return ma_format_s32;
8654 }
8655 if (pWFEX->Samples.wValidBitsPerSample == 24) {
8656 if (pWFEX->Format.wBitsPerSample == 32) {
8657 /*return ma_format_s24_32;*/
8658 }
8659 if (pWFEX->Format.wBitsPerSample == 24) {
8660 return ma_format_s24;
8661 }
8662 }
8663 if (pWFEX->Samples.wValidBitsPerSample == 16) {
8664 return ma_format_s16;
8665 }
8666 if (pWFEX->Samples.wValidBitsPerSample == 8) {
8667 return ma_format_u8;
8668 }
8669 }
8670 if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
8671 if (pWFEX->Samples.wValidBitsPerSample == 32) {
8672 return ma_format_f32;
8673 }
8674 /*
8675 if (pWFEX->Samples.wValidBitsPerSample == 64) {
8676 return ma_format_f64;
8677 }
8678 */
8679 }
8680 } else {
8681 if (pWF->wFormatTag == WAVE_FORMAT_PCM) {
8682 if (pWF->wBitsPerSample == 32) {
8683 return ma_format_s32;
8684 }
8685 if (pWF->wBitsPerSample == 24) {
8686 return ma_format_s24;
8687 }
8688 if (pWF->wBitsPerSample == 16) {
8689 return ma_format_s16;
8690 }
8691 if (pWF->wBitsPerSample == 8) {
8692 return ma_format_u8;
8693 }
8694 }
8695 if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
8696 if (pWF->wBitsPerSample == 32) {
8697 return ma_format_f32;
8698 }
8699 if (pWF->wBitsPerSample == 64) {
8700 /*return ma_format_f64;*/
8701 }
8702 }
8703 }
8704
8705 return ma_format_unknown;
8706}
8707#endif
8708
8709
8710/*******************************************************************************
8711
8712WASAPI Backend
8713
8714*******************************************************************************/
8715#ifdef MA_HAS_WASAPI
8716#if 0
8717#if defined(_MSC_VER)
8718 #pragma warning(push)
8719 #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */
8720#endif
8721#include <audioclient.h>
8722#include <mmdeviceapi.h>
8723#if defined(_MSC_VER)
8724 #pragma warning(pop)
8725#endif
8726#endif /* 0 */
8727
8728/* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */
8729#define MA_WIN32_WINNT_VISTA 0x0600
8730#define MA_VER_MINORVERSION 0x01
8731#define MA_VER_MAJORVERSION 0x02
8732#define MA_VER_SERVICEPACKMAJOR 0x20
8733#define MA_VER_GREATER_EQUAL 0x03
8734
8735typedef struct {
8736 DWORD dwOSVersionInfoSize;
8737 DWORD dwMajorVersion;
8738 DWORD dwMinorVersion;
8739 DWORD dwBuildNumber;
8740 DWORD dwPlatformId;
8741 WCHAR szCSDVersion[128];
8742 WORD wServicePackMajor;
8743 WORD wServicePackMinor;
8744 WORD wSuiteMask;
8745 BYTE wProductType;
8746 BYTE wReserved;
8747} ma_OSVERSIONINFOEXW;
8748
8749typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask);
8750typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask);
8751
8752
8753#ifndef PROPERTYKEY_DEFINED
8754#define PROPERTYKEY_DEFINED
8755typedef struct
8756{
8757 GUID fmtid;
8758 DWORD pid;
8759} PROPERTYKEY;
8760#endif
8761
8762/* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */
8763static MA_INLINE void ma_PropVariantInit(PROPVARIANT* pProp)
8764{
8765 MA_ZERO_OBJECT(pProp);
8766}
8767
8768
8769static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14};
8770static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0};
8771
8772static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */
8773static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */
8774
8775static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */
8776static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */
8777static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */
8778static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */
8779static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */
8780static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */
8781#ifndef MA_WIN32_DESKTOP
8782static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */
8783static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */
8784static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */
8785#endif
8786
8787static const IID MA_CLSID_MMDeviceEnumerator_Instance = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */
8788static const IID MA_IID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */
8789#ifdef __cplusplus
8790#define MA_CLSID_MMDeviceEnumerator MA_CLSID_MMDeviceEnumerator_Instance
8791#define MA_IID_IMMDeviceEnumerator MA_IID_IMMDeviceEnumerator_Instance
8792#else
8793#define MA_CLSID_MMDeviceEnumerator &MA_CLSID_MMDeviceEnumerator_Instance
8794#define MA_IID_IMMDeviceEnumerator &MA_IID_IMMDeviceEnumerator_Instance
8795#endif
8796
8797typedef struct ma_IUnknown ma_IUnknown;
8798#ifdef MA_WIN32_DESKTOP
8799#define MA_MM_DEVICE_STATE_ACTIVE 1
8800#define MA_MM_DEVICE_STATE_DISABLED 2
8801#define MA_MM_DEVICE_STATE_NOTPRESENT 4
8802#define MA_MM_DEVICE_STATE_UNPLUGGED 8
8803
8804typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator;
8805typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection;
8806typedef struct ma_IMMDevice ma_IMMDevice;
8807#else
8808typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler;
8809typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation;
8810#endif
8811typedef struct ma_IPropertyStore ma_IPropertyStore;
8812typedef struct ma_IAudioClient ma_IAudioClient;
8813typedef struct ma_IAudioClient2 ma_IAudioClient2;
8814typedef struct ma_IAudioClient3 ma_IAudioClient3;
8815typedef struct ma_IAudioRenderClient ma_IAudioRenderClient;
8816typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient;
8817
8818typedef ma_int64 MA_REFERENCE_TIME;
8819
8820#define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000
8821#define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000
8822#define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000
8823#define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000
8824#define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
8825#define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
8826#define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
8827#define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000
8828#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000
8829#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000
8830
8831/* We only care about a few error codes. */
8832#define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD (-2004287456)
8833#define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED (-2004287463)
8834#define MA_AUDCLNT_S_BUFFER_EMPTY (143196161)
8835#define MA_AUDCLNT_E_DEVICE_IN_USE (-2004287478)
8836
8837/* Buffer flags. */
8838#define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1
8839#define MA_AUDCLNT_BUFFERFLAGS_SILENT 2
8840#define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4
8841
8842typedef enum
8843{
8844 ma_eRender = 0,
8845 ma_eCapture = 1,
8846 ma_eAll = 2
8847} ma_EDataFlow;
8848
8849typedef enum
8850{
8851 ma_eConsole = 0,
8852 ma_eMultimedia = 1,
8853 ma_eCommunications = 2
8854} ma_ERole;
8855
8856typedef enum
8857{
8858 MA_AUDCLNT_SHAREMODE_SHARED,
8859 MA_AUDCLNT_SHAREMODE_EXCLUSIVE
8860} MA_AUDCLNT_SHAREMODE;
8861
8862typedef enum
8863{
8864 MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */
8865} MA_AUDIO_STREAM_CATEGORY;
8866
8867typedef struct
8868{
8869 UINT32 cbSize;
8870 BOOL bIsOffload;
8871 MA_AUDIO_STREAM_CATEGORY eCategory;
8872} ma_AudioClientProperties;
8873
8874/* IUnknown */
8875typedef struct
8876{
8877 /* IUnknown */
8878 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject);
8879 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis);
8880 ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis);
8881} ma_IUnknownVtbl;
8882struct ma_IUnknown
8883{
8884 ma_IUnknownVtbl* lpVtbl;
8885};
8886static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
8887static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); }
8888static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); }
8889
8890#ifdef MA_WIN32_DESKTOP
8891 /* IMMNotificationClient */
8892 typedef struct
8893 {
8894 /* IUnknown */
8895 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject);
8896 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis);
8897 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis);
8898
8899 /* IMMNotificationClient */
8900 HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState);
8901 HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID);
8902 HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID);
8903 HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID);
8904 HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key);
8905 } ma_IMMNotificationClientVtbl;
8906
8907 /* IMMDeviceEnumerator */
8908 typedef struct
8909 {
8910 /* IUnknown */
8911 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject);
8912 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis);
8913 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis);
8914
8915 /* IMMDeviceEnumerator */
8916 HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices);
8917 HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint);
8918 HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice);
8919 HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
8920 HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
8921 } ma_IMMDeviceEnumeratorVtbl;
8922 struct ma_IMMDeviceEnumerator
8923 {
8924 ma_IMMDeviceEnumeratorVtbl* lpVtbl;
8925 };
8926 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
8927 static MA_INLINE ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); }
8928 static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); }
8929 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); }
8930 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); }
8931 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); }
8932 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); }
8933 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); }
8934
8935
8936 /* IMMDeviceCollection */
8937 typedef struct
8938 {
8939 /* IUnknown */
8940 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject);
8941 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis);
8942 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis);
8943
8944 /* IMMDeviceCollection */
8945 HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices);
8946 HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice);
8947 } ma_IMMDeviceCollectionVtbl;
8948 struct ma_IMMDeviceCollection
8949 {
8950 ma_IMMDeviceCollectionVtbl* lpVtbl;
8951 };
8952 static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
8953 static MA_INLINE ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); }
8954 static MA_INLINE ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); }
8955 static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); }
8956 static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); }
8957
8958
8959 /* IMMDevice */
8960 typedef struct
8961 {
8962 /* IUnknown */
8963 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject);
8964 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis);
8965 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis);
8966
8967 /* IMMDevice */
8968 HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface);
8969 HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties);
8970 HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, LPWSTR *pID);
8971 HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState);
8972 } ma_IMMDeviceVtbl;
8973 struct ma_IMMDevice
8974 {
8975 ma_IMMDeviceVtbl* lpVtbl;
8976 };
8977 static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
8978 static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); }
8979 static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); }
8980 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); }
8981 static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); }
8982 static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, LPWSTR *pID) { return pThis->lpVtbl->GetId(pThis, pID); }
8983 static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); }
8984#else
8985 /* IActivateAudioInterfaceAsyncOperation */
8986 typedef struct
8987 {
8988 /* IUnknown */
8989 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject);
8990 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
8991 ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
8992
8993 /* IActivateAudioInterfaceAsyncOperation */
8994 HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface);
8995 } ma_IActivateAudioInterfaceAsyncOperationVtbl;
8996 struct ma_IActivateAudioInterfaceAsyncOperation
8997 {
8998 ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl;
8999 };
9000 static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
9001 static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); }
9002 static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); }
9003 static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); }
9004#endif
9005
9006/* IPropertyStore */
9007typedef struct
9008{
9009 /* IUnknown */
9010 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject);
9011 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis);
9012 ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis);
9013
9014 /* IPropertyStore */
9015 HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount);
9016 HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey);
9017 HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar);
9018 HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar);
9019 HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis);
9020} ma_IPropertyStoreVtbl;
9021struct ma_IPropertyStore
9022{
9023 ma_IPropertyStoreVtbl* lpVtbl;
9024};
9025static MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
9026static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); }
9027static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); }
9028static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); }
9029static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); }
9030static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); }
9031static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); }
9032static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); }
9033
9034
9035/* IAudioClient */
9036typedef struct
9037{
9038 /* IUnknown */
9039 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject);
9040 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis);
9041 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis);
9042
9043 /* IAudioClient */
9044 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);
9045 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames);
9046 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency);
9047 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames);
9048 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
9049 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat);
9050 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
9051 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis);
9052 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis);
9053 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis);
9054 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle);
9055 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp);
9056} ma_IAudioClientVtbl;
9057struct ma_IAudioClient
9058{
9059 ma_IAudioClientVtbl* lpVtbl;
9060};
9061static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
9062static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
9063static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); }
9064static 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); }
9065static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
9066static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
9067static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
9068static 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); }
9069static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
9070static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
9071static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); }
9072static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); }
9073static MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); }
9074static MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
9075static MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
9076
9077/* IAudioClient2 */
9078typedef struct
9079{
9080 /* IUnknown */
9081 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject);
9082 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis);
9083 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis);
9084
9085 /* IAudioClient */
9086 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);
9087 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames);
9088 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency);
9089 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames);
9090 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
9091 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat);
9092 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
9093 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis);
9094 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis);
9095 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis);
9096 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle);
9097 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp);
9098
9099 /* IAudioClient2 */
9100 HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
9101 HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties);
9102 HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
9103} ma_IAudioClient2Vtbl;
9104struct ma_IAudioClient2
9105{
9106 ma_IAudioClient2Vtbl* lpVtbl;
9107};
9108static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
9109static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); }
9110static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); }
9111static 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); }
9112static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
9113static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
9114static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
9115static 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); }
9116static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
9117static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
9118static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); }
9119static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); }
9120static MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); }
9121static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
9122static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
9123static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
9124static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
9125static 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); }
9126
9127
9128/* IAudioClient3 */
9129typedef struct
9130{
9131 /* IUnknown */
9132 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject);
9133 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis);
9134 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis);
9135
9136 /* IAudioClient */
9137 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);
9138 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames);
9139 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency);
9140 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames);
9141 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
9142 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat);
9143 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
9144 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis);
9145 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis);
9146 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis);
9147 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle);
9148 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp);
9149
9150 /* IAudioClient2 */
9151 HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
9152 HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties);
9153 HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
9154
9155 /* IAudioClient3 */
9156 HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, UINT32* pDefaultPeriodInFrames, UINT32* pFundamentalPeriodInFrames, UINT32* pMinPeriodInFrames, UINT32* pMaxPeriodInFrames);
9157 HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, UINT32* pCurrentPeriodInFrames);
9158 HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, UINT32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
9159} ma_IAudioClient3Vtbl;
9160struct ma_IAudioClient3
9161{
9162 ma_IAudioClient3Vtbl* lpVtbl;
9163};
9164static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
9165static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); }
9166static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); }
9167static 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); }
9168static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
9169static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
9170static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
9171static 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); }
9172static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
9173static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
9174static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); }
9175static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); }
9176static MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); }
9177static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
9178static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
9179static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
9180static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
9181static 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); }
9182static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, UINT32* pDefaultPeriodInFrames, UINT32* pFundamentalPeriodInFrames, UINT32* pMinPeriodInFrames, UINT32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); }
9183static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, UINT32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); }
9184static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, UINT32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); }
9185
9186
9187/* IAudioRenderClient */
9188typedef struct
9189{
9190 /* IUnknown */
9191 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject);
9192 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis);
9193 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis);
9194
9195 /* IAudioRenderClient */
9196 HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData);
9197 HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags);
9198} ma_IAudioRenderClientVtbl;
9199struct ma_IAudioRenderClient
9200{
9201 ma_IAudioRenderClientVtbl* lpVtbl;
9202};
9203static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
9204static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
9205static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); }
9206static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); }
9207static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); }
9208
9209
9210/* IAudioCaptureClient */
9211typedef struct
9212{
9213 /* IUnknown */
9214 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject);
9215 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis);
9216 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis);
9217
9218 /* IAudioRenderClient */
9219 HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition);
9220 HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead);
9221 HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket);
9222} ma_IAudioCaptureClientVtbl;
9223struct ma_IAudioCaptureClient
9224{
9225 ma_IAudioCaptureClientVtbl* lpVtbl;
9226};
9227static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
9228static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
9229static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); }
9230static 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); }
9231static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); }
9232static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); }
9233
9234#ifndef MA_WIN32_DESKTOP
9235#include <mmdeviceapi.h>
9236typedef struct ma_completion_handler_uwp ma_completion_handler_uwp;
9237
9238typedef struct
9239{
9240 /* IUnknown */
9241 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject);
9242 ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis);
9243 ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis);
9244
9245 /* IActivateAudioInterfaceCompletionHandler */
9246 HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation);
9247} ma_completion_handler_uwp_vtbl;
9248struct ma_completion_handler_uwp
9249{
9250 ma_completion_handler_uwp_vtbl* lpVtbl;
9251 ma_uint32 counter;
9252 HANDLE hEvent;
9253};
9254
9255static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject)
9256{
9257 /*
9258 We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To
9259 "implement" this, we just make sure we return pThis when the IAgileObject is requested.
9260 */
9261 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)) {
9262 *ppObject = NULL;
9263 return E_NOINTERFACE;
9264 }
9265
9266 /* Getting here means the IID is IUnknown or IMMNotificationClient. */
9267 *ppObject = (void*)pThis;
9268 ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis);
9269 return S_OK;
9270}
9271
9272static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis)
9273{
9274 return (ULONG)ma_atomic_increment_32(&pThis->counter);
9275}
9276
9277static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis)
9278{
9279 ma_uint32 newRefCount = ma_atomic_decrement_32(&pThis->counter);
9280 if (newRefCount == 0) {
9281 return 0; /* We don't free anything here because we never allocate the object on the heap. */
9282 }
9283
9284 return (ULONG)newRefCount;
9285}
9286
9287static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation)
9288{
9289 (void)pActivateOperation;
9290 SetEvent(pThis->hEvent);
9291 return S_OK;
9292}
9293
9294
9295static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = {
9296 ma_completion_handler_uwp_QueryInterface,
9297 ma_completion_handler_uwp_AddRef,
9298 ma_completion_handler_uwp_Release,
9299 ma_completion_handler_uwp_ActivateCompleted
9300};
9301
9302static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler)
9303{
9304 MA_ASSERT(pHandler != NULL);
9305 MA_ZERO_OBJECT(pHandler);
9306
9307 pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance;
9308 pHandler->counter = 1;
9309 pHandler->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
9310 if (pHandler->hEvent == NULL) {
9311 return MA_ERROR;
9312 }
9313
9314 return MA_SUCCESS;
9315}
9316
9317static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler)
9318{
9319 if (pHandler->hEvent != NULL) {
9320 CloseHandle(pHandler->hEvent);
9321 }
9322}
9323
9324static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler)
9325{
9326 WaitForSingleObject(pHandler->hEvent, INFINITE);
9327}
9328#endif /* !MA_WIN32_DESKTOP */
9329
9330/* We need a virtual table for our notification client object that's used for detecting changes to the default device. */
9331#ifdef MA_WIN32_DESKTOP
9332static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject)
9333{
9334 /*
9335 We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else
9336 we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK.
9337 */
9338 if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) {
9339 *ppObject = NULL;
9340 return E_NOINTERFACE;
9341 }
9342
9343 /* Getting here means the IID is IUnknown or IMMNotificationClient. */
9344 *ppObject = (void*)pThis;
9345 ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis);
9346 return S_OK;
9347}
9348
9349static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis)
9350{
9351 return (ULONG)ma_atomic_increment_32(&pThis->counter);
9352}
9353
9354static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis)
9355{
9356 ma_uint32 newRefCount = ma_atomic_decrement_32(&pThis->counter);
9357 if (newRefCount == 0) {
9358 return 0; /* We don't free anything here because we never allocate the object on the heap. */
9359 }
9360
9361 return (ULONG)newRefCount;
9362}
9363
9364
9365static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState)
9366{
9367#ifdef MA_DEBUG_OUTPUT
9368 printf("IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);
9369#endif
9370
9371 (void)pThis;
9372 (void)pDeviceID;
9373 (void)dwNewState;
9374 return S_OK;
9375}
9376
9377static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
9378{
9379#ifdef MA_DEBUG_OUTPUT
9380 printf("IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");
9381#endif
9382
9383 /* We don't need to worry about this event for our purposes. */
9384 (void)pThis;
9385 (void)pDeviceID;
9386 return S_OK;
9387}
9388
9389static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
9390{
9391#ifdef MA_DEBUG_OUTPUT
9392 printf("IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");
9393#endif
9394
9395 /* We don't need to worry about this event for our purposes. */
9396 (void)pThis;
9397 (void)pDeviceID;
9398 return S_OK;
9399}
9400
9401static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID)
9402{
9403#ifdef MA_DEBUG_OUTPUT
9404 printf("IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");
9405#endif
9406
9407 /* We only ever use the eConsole role in miniaudio. */
9408 if (role != ma_eConsole) {
9409 return S_OK;
9410 }
9411
9412 /* We only care about devices with the same data flow and role as the current device. */
9413 if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) ||
9414 (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture)) {
9415 return S_OK;
9416 }
9417
9418 /* Don't do automatic stream routing if we're not allowed. */
9419 if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) ||
9420 (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) {
9421 return S_OK;
9422 }
9423
9424 /*
9425 Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to
9426 AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once
9427 it's fixed.
9428 */
9429 if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) ||
9430 (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) {
9431 return S_OK;
9432 }
9433
9434 /*
9435 We don't change the device here - we change it in the worker thread to keep synchronization simple. To do this I'm just setting a flag to
9436 indicate that the default device has changed. Loopback devices are treated as capture devices so we need to do a bit of a dance to handle
9437 that properly.
9438 */
9439 if (dataFlow == ma_eRender && pThis->pDevice->type != ma_device_type_loopback) {
9440 ma_atomic_exchange_32(&pThis->pDevice->wasapi.hasDefaultPlaybackDeviceChanged, MA_TRUE);
9441 }
9442 if (dataFlow == ma_eCapture || pThis->pDevice->type == ma_device_type_loopback) {
9443 ma_atomic_exchange_32(&pThis->pDevice->wasapi.hasDefaultCaptureDeviceChanged, MA_TRUE);
9444 }
9445
9446 (void)pDefaultDeviceID;
9447 return S_OK;
9448}
9449
9450static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key)
9451{
9452#ifdef MA_DEBUG_OUTPUT
9453 printf("IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");
9454#endif
9455
9456 (void)pThis;
9457 (void)pDeviceID;
9458 (void)key;
9459 return S_OK;
9460}
9461
9462static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = {
9463 ma_IMMNotificationClient_QueryInterface,
9464 ma_IMMNotificationClient_AddRef,
9465 ma_IMMNotificationClient_Release,
9466 ma_IMMNotificationClient_OnDeviceStateChanged,
9467 ma_IMMNotificationClient_OnDeviceAdded,
9468 ma_IMMNotificationClient_OnDeviceRemoved,
9469 ma_IMMNotificationClient_OnDefaultDeviceChanged,
9470 ma_IMMNotificationClient_OnPropertyValueChanged
9471};
9472#endif /* MA_WIN32_DESKTOP */
9473
9474#ifdef MA_WIN32_DESKTOP
9475typedef ma_IMMDevice ma_WASAPIDeviceInterface;
9476#else
9477typedef ma_IUnknown ma_WASAPIDeviceInterface;
9478#endif
9479
9480
9481
9482static ma_bool32 ma_context_is_device_id_equal__wasapi(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
9483{
9484 MA_ASSERT(pContext != NULL);
9485 MA_ASSERT(pID0 != NULL);
9486 MA_ASSERT(pID1 != NULL);
9487 (void)pContext;
9488
9489 return memcmp(pID0->wasapi, pID1->wasapi, sizeof(pID0->wasapi)) == 0;
9490}
9491
9492static void ma_set_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, ma_device_info* pInfo)
9493{
9494 MA_ASSERT(pWF != NULL);
9495 MA_ASSERT(pInfo != NULL);
9496
9497 pInfo->formatCount = 1;
9498 pInfo->formats[0] = ma_format_from_WAVEFORMATEX(pWF);
9499 pInfo->minChannels = pWF->nChannels;
9500 pInfo->maxChannels = pWF->nChannels;
9501 pInfo->minSampleRate = pWF->nSamplesPerSec;
9502 pInfo->maxSampleRate = pWF->nSamplesPerSec;
9503}
9504
9505static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_share_mode shareMode, ma_device_info* pInfo)
9506{
9507 MA_ASSERT(pAudioClient != NULL);
9508 MA_ASSERT(pInfo != NULL);
9509
9510 /* We use a different technique to retrieve the device information depending on whether or not we are using shared or exclusive mode. */
9511 if (shareMode == ma_share_mode_shared) {
9512 /* Shared Mode. We use GetMixFormat() here. */
9513 WAVEFORMATEX* pWF = NULL;
9514 HRESULT hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (WAVEFORMATEX**)&pWF);
9515 if (SUCCEEDED(hr)) {
9516 ma_set_device_info_from_WAVEFORMATEX(pWF, pInfo);
9517 return MA_SUCCESS;
9518 } else {
9519 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9520 }
9521 } else {
9522 /* Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently support on UWP. */
9523#ifdef MA_WIN32_DESKTOP
9524 /*
9525 The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is
9526 correct which will simplify our searching.
9527 */
9528 ma_IPropertyStore *pProperties;
9529 HRESULT hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties);
9530 if (SUCCEEDED(hr)) {
9531 PROPVARIANT var;
9532 ma_PropVariantInit(&var);
9533
9534 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var);
9535 if (SUCCEEDED(hr)) {
9536 WAVEFORMATEX* pWF = (WAVEFORMATEX*)var.blob.pBlobData;
9537 ma_set_device_info_from_WAVEFORMATEX(pWF, pInfo);
9538
9539 /*
9540 In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format
9541 first. If this fails, fall back to a search.
9542 */
9543 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL);
9544 ma_PropVariantClear(pContext, &var);
9545
9546 if (FAILED(hr)) {
9547 /*
9548 The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel
9549 count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format.
9550 */
9551 ma_uint32 channels = pInfo->minChannels;
9552 ma_format formatsToSearch[] = {
9553 ma_format_s16,
9554 ma_format_s24,
9555 /*ma_format_s24_32,*/
9556 ma_format_f32,
9557 ma_format_s32,
9558 ma_format_u8
9559 };
9560 ma_channel defaultChannelMap[MA_MAX_CHANNELS];
9561 WAVEFORMATEXTENSIBLE wf;
9562 ma_bool32 found;
9563 ma_uint32 iFormat;
9564
9565 ma_get_standard_channel_map(ma_standard_channel_map_microsoft, channels, defaultChannelMap);
9566
9567 MA_ZERO_OBJECT(&wf);
9568 wf.Format.cbSize = sizeof(wf);
9569 wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
9570 wf.Format.nChannels = (WORD)channels;
9571 wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels);
9572
9573 found = MA_FALSE;
9574 for (iFormat = 0; iFormat < ma_countof(formatsToSearch); ++iFormat) {
9575 ma_format format = formatsToSearch[iFormat];
9576 ma_uint32 iSampleRate;
9577
9578 wf.Format.wBitsPerSample = (WORD)ma_get_bytes_per_sample(format)*8;
9579 wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
9580 wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
9581 wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.Format.wBitsPerSample;
9582 if (format == ma_format_f32) {
9583 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
9584 } else {
9585 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
9586 }
9587
9588 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) {
9589 wf.Format.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate];
9590
9591 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*)&wf, NULL);
9592 if (SUCCEEDED(hr)) {
9593 ma_set_device_info_from_WAVEFORMATEX((WAVEFORMATEX*)&wf, pInfo);
9594 found = MA_TRUE;
9595 break;
9596 }
9597 }
9598
9599 if (found) {
9600 break;
9601 }
9602 }
9603
9604 if (!found) {
9605 ma_IPropertyStore_Release(pProperties);
9606 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to find suitable device format for device info retrieval.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9607 }
9608 }
9609 } else {
9610 ma_IPropertyStore_Release(pProperties);
9611 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve device format for device info retrieval.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9612 }
9613
9614 ma_IPropertyStore_Release(pProperties);
9615 } else {
9616 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to open property store for device info retrieval.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9617 }
9618
9619 return MA_SUCCESS;
9620#else
9621 /* Exclusive mode not fully supported in UWP right now. */
9622 return MA_ERROR;
9623#endif
9624 }
9625}
9626
9627#ifdef MA_WIN32_DESKTOP
9628static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType)
9629{
9630 if (deviceType == ma_device_type_playback) {
9631 return ma_eRender;
9632 } else if (deviceType == ma_device_type_capture) {
9633 return ma_eCapture;
9634 } else {
9635 MA_ASSERT(MA_FALSE);
9636 return ma_eRender; /* Should never hit this. */
9637 }
9638}
9639
9640static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator)
9641{
9642 HRESULT hr;
9643 ma_IMMDeviceEnumerator* pDeviceEnumerator;
9644
9645 MA_ASSERT(pContext != NULL);
9646 MA_ASSERT(ppDeviceEnumerator != NULL);
9647
9648 hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
9649 if (FAILED(hr)) {
9650 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", MA_ERROR);
9651 }
9652
9653 *ppDeviceEnumerator = pDeviceEnumerator;
9654
9655 return MA_SUCCESS;
9656}
9657
9658static LPWSTR ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType)
9659{
9660 HRESULT hr;
9661 ma_IMMDevice* pMMDefaultDevice = NULL;
9662 LPWSTR pDefaultDeviceID = NULL;
9663 ma_EDataFlow dataFlow;
9664 ma_ERole role;
9665
9666 MA_ASSERT(pContext != NULL);
9667 MA_ASSERT(pDeviceEnumerator != NULL);
9668
9669 /* Grab the EDataFlow type from the device type. */
9670 dataFlow = ma_device_type_to_EDataFlow(deviceType);
9671
9672 /* The role is always eConsole, but we may make this configurable later. */
9673 role = ma_eConsole;
9674
9675 hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice);
9676 if (FAILED(hr)) {
9677 return NULL;
9678 }
9679
9680 hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID);
9681
9682 ma_IMMDevice_Release(pMMDefaultDevice);
9683 pMMDefaultDevice = NULL;
9684
9685 if (FAILED(hr)) {
9686 return NULL;
9687 }
9688
9689 return pDefaultDeviceID;
9690}
9691
9692static LPWSTR ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */
9693{
9694 ma_result result;
9695 ma_IMMDeviceEnumerator* pDeviceEnumerator;
9696 LPWSTR pDefaultDeviceID = NULL;
9697
9698 MA_ASSERT(pContext != NULL);
9699
9700 result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator);
9701 if (result != MA_SUCCESS) {
9702 return NULL;
9703 }
9704
9705 pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
9706
9707 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
9708 return pDefaultDeviceID;
9709}
9710
9711static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice)
9712{
9713 ma_IMMDeviceEnumerator* pDeviceEnumerator;
9714 HRESULT hr;
9715
9716 MA_ASSERT(pContext != NULL);
9717 MA_ASSERT(ppMMDevice != NULL);
9718
9719 hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
9720 if (FAILED(hr)) {
9721 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.", MA_FAILED_TO_INIT_BACKEND);
9722 }
9723
9724 if (pDeviceID == NULL) {
9725 hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice);
9726 } else {
9727 hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice);
9728 }
9729
9730 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
9731 if (FAILED(hr)) {
9732 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9733 }
9734
9735 return MA_SUCCESS;
9736}
9737
9738static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_share_mode shareMode, LPWSTR pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo)
9739{
9740 LPWSTR pDeviceID;
9741 HRESULT hr;
9742
9743 MA_ASSERT(pContext != NULL);
9744 MA_ASSERT(pMMDevice != NULL);
9745 MA_ASSERT(pInfo != NULL);
9746
9747 /* ID. */
9748 hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceID);
9749 if (SUCCEEDED(hr)) {
9750 size_t idlen = wcslen(pDeviceID);
9751 if (idlen+1 > ma_countof(pInfo->id.wasapi)) {
9752 ma_CoTaskMemFree(pContext, pDeviceID);
9753 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. */
9754 return MA_ERROR;
9755 }
9756
9757 MA_COPY_MEMORY(pInfo->id.wasapi, pDeviceID, idlen * sizeof(wchar_t));
9758 pInfo->id.wasapi[idlen] = '\0';
9759
9760 if (pDefaultDeviceID != NULL) {
9761 if (wcscmp(pDeviceID, pDefaultDeviceID) == 0) {
9762 /* It's a default device. */
9763 pInfo->_private.isDefault = MA_TRUE;
9764 }
9765 }
9766
9767 ma_CoTaskMemFree(pContext, pDeviceID);
9768 }
9769
9770 {
9771 ma_IPropertyStore *pProperties;
9772 hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties);
9773 if (SUCCEEDED(hr)) {
9774 PROPVARIANT var;
9775
9776 /* Description / Friendly Name */
9777 ma_PropVariantInit(&var);
9778 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var);
9779 if (SUCCEEDED(hr)) {
9780 WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE);
9781 ma_PropVariantClear(pContext, &var);
9782 }
9783
9784 ma_IPropertyStore_Release(pProperties);
9785 }
9786 }
9787
9788 /* Format */
9789 if (!onlySimpleInfo) {
9790 ma_IAudioClient* pAudioClient;
9791 hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
9792 if (SUCCEEDED(hr)) {
9793 ma_result result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, shareMode, pInfo);
9794
9795 ma_IAudioClient_Release(pAudioClient);
9796 return result;
9797 } else {
9798 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9799 }
9800 }
9801
9802 return MA_SUCCESS;
9803}
9804
9805static 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)
9806{
9807 ma_result result = MA_SUCCESS;
9808 UINT deviceCount;
9809 HRESULT hr;
9810 ma_uint32 iDevice;
9811 LPWSTR pDefaultDeviceID = NULL;
9812 ma_IMMDeviceCollection* pDeviceCollection = NULL;
9813
9814 MA_ASSERT(pContext != NULL);
9815 MA_ASSERT(callback != NULL);
9816
9817 /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */
9818 pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
9819
9820 /* We need to enumerate the devices which returns a device collection. */
9821 hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection);
9822 if (SUCCEEDED(hr)) {
9823 hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount);
9824 if (FAILED(hr)) {
9825 result = ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.", MA_NO_DEVICE);
9826 goto done;
9827 }
9828
9829 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
9830 ma_device_info deviceInfo;
9831 ma_IMMDevice* pMMDevice;
9832
9833 MA_ZERO_OBJECT(&deviceInfo);
9834
9835 hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice);
9836 if (SUCCEEDED(hr)) {
9837 result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, ma_share_mode_shared, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */
9838
9839 ma_IMMDevice_Release(pMMDevice);
9840 if (result == MA_SUCCESS) {
9841 ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
9842 if (cbResult == MA_FALSE) {
9843 break;
9844 }
9845 }
9846 }
9847 }
9848 }
9849
9850done:
9851 if (pDefaultDeviceID != NULL) {
9852 ma_CoTaskMemFree(pContext, pDefaultDeviceID);
9853 pDefaultDeviceID = NULL;
9854 }
9855
9856 if (pDeviceCollection != NULL) {
9857 ma_IMMDeviceCollection_Release(pDeviceCollection);
9858 pDeviceCollection = NULL;
9859 }
9860
9861 return result;
9862}
9863
9864static 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)
9865{
9866 ma_result result;
9867 HRESULT hr;
9868
9869 MA_ASSERT(pContext != NULL);
9870 MA_ASSERT(ppAudioClient != NULL);
9871 MA_ASSERT(ppMMDevice != NULL);
9872
9873 result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice);
9874 if (result != MA_SUCCESS) {
9875 return result;
9876 }
9877
9878 hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)ppAudioClient);
9879 if (FAILED(hr)) {
9880 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
9881 }
9882
9883 return MA_SUCCESS;
9884}
9885#else
9886static 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)
9887{
9888 ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;
9889 ma_completion_handler_uwp completionHandler;
9890 IID iid;
9891 LPOLESTR iidStr;
9892 HRESULT hr;
9893 ma_result result;
9894 HRESULT activateResult;
9895 ma_IUnknown* pActivatedInterface;
9896
9897 MA_ASSERT(pContext != NULL);
9898 MA_ASSERT(ppAudioClient != NULL);
9899
9900 if (pDeviceID != NULL) {
9901 MA_COPY_MEMORY(&iid, pDeviceID->wasapi, sizeof(iid));
9902 } else {
9903 if (deviceType == ma_device_type_playback) {
9904 iid = MA_IID_DEVINTERFACE_AUDIO_RENDER;
9905 } else {
9906 iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE;
9907 }
9908 }
9909
9910#if defined(__cplusplus)
9911 hr = StringFromIID(iid, &iidStr);
9912#else
9913 hr = StringFromIID(&iid, &iidStr);
9914#endif
9915 if (FAILED(hr)) {
9916 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_OUT_OF_MEMORY);
9917 }
9918
9919 result = ma_completion_handler_uwp_init(&completionHandler);
9920 if (result != MA_SUCCESS) {
9921 ma_CoTaskMemFree(pContext, iidStr);
9922 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9923 }
9924
9925#if defined(__cplusplus)
9926 hr = ActivateAudioInterfaceAsync(iidStr, MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
9927#else
9928 hr = ActivateAudioInterfaceAsync(iidStr, &MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
9929#endif
9930 if (FAILED(hr)) {
9931 ma_completion_handler_uwp_uninit(&completionHandler);
9932 ma_CoTaskMemFree(pContext, iidStr);
9933 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9934 }
9935
9936 ma_CoTaskMemFree(pContext, iidStr);
9937
9938 /* Wait for the async operation for finish. */
9939 ma_completion_handler_uwp_wait(&completionHandler);
9940 ma_completion_handler_uwp_uninit(&completionHandler);
9941
9942 hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface);
9943 ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
9944
9945 if (FAILED(hr) || FAILED(activateResult)) {
9946 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9947 }
9948
9949 /* Here is where we grab the IAudioClient interface. */
9950 hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient);
9951 if (FAILED(hr)) {
9952 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9953 }
9954
9955 if (ppActivatedInterface) {
9956 *ppActivatedInterface = pActivatedInterface;
9957 } else {
9958 ma_IUnknown_Release(pActivatedInterface);
9959 }
9960
9961 return MA_SUCCESS;
9962}
9963#endif
9964
9965static 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)
9966{
9967#ifdef MA_WIN32_DESKTOP
9968 return ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface);
9969#else
9970 return ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface);
9971#endif
9972}
9973
9974
9975static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
9976{
9977 /* Different enumeration for desktop and UWP. */
9978#ifdef MA_WIN32_DESKTOP
9979 /* Desktop */
9980 HRESULT hr;
9981 ma_IMMDeviceEnumerator* pDeviceEnumerator;
9982
9983 hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
9984 if (FAILED(hr)) {
9985 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9986 }
9987
9988 ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData);
9989 ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData);
9990
9991 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
9992#else
9993 /*
9994 UWP
9995
9996 The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate
9997 over devices without using MMDevice, I'm restricting devices to defaults.
9998
9999 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/
10000 */
10001 if (callback) {
10002 ma_bool32 cbResult = MA_TRUE;
10003
10004 /* Playback. */
10005 if (cbResult) {
10006 ma_device_info deviceInfo;
10007 MA_ZERO_OBJECT(&deviceInfo);
10008 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
10009 deviceInfo._private.isDefault = MA_TRUE;
10010 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
10011 }
10012
10013 /* Capture. */
10014 if (cbResult) {
10015 ma_device_info deviceInfo;
10016 MA_ZERO_OBJECT(&deviceInfo);
10017 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
10018 deviceInfo._private.isDefault = MA_TRUE;
10019 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
10020 }
10021 }
10022#endif
10023
10024 return MA_SUCCESS;
10025}
10026
10027static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
10028{
10029#ifdef MA_WIN32_DESKTOP
10030 ma_result result;
10031 ma_IMMDevice* pMMDevice = NULL;
10032 LPWSTR pDefaultDeviceID = NULL;
10033
10034 result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice);
10035 if (result != MA_SUCCESS) {
10036 return result;
10037 }
10038
10039 /* We need the default device ID so we can set the isDefault flag in the device info. */
10040 pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType);
10041
10042 result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, shareMode, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */
10043
10044 if (pDefaultDeviceID != NULL) {
10045 ma_CoTaskMemFree(pContext, pDefaultDeviceID);
10046 pDefaultDeviceID = NULL;
10047 }
10048
10049 ma_IMMDevice_Release(pMMDevice);
10050
10051 return result;
10052#else
10053 ma_IAudioClient* pAudioClient;
10054 ma_result result;
10055
10056 /* UWP currently only uses default devices. */
10057 if (deviceType == ma_device_type_playback) {
10058 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
10059 } else {
10060 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
10061 }
10062
10063 /* Not currently supporting exclusive mode on UWP. */
10064 if (shareMode == ma_share_mode_exclusive) {
10065 return MA_ERROR;
10066 }
10067
10068 result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, &pAudioClient, NULL);
10069 if (result != MA_SUCCESS) {
10070 return result;
10071 }
10072
10073 result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, shareMode, pDeviceInfo);
10074
10075 pDeviceInfo->_private.isDefault = MA_TRUE; /* UWP only supports default devices. */
10076
10077 ma_IAudioClient_Release(pAudioClient);
10078 return result;
10079#endif
10080}
10081
10082static void ma_device_uninit__wasapi(ma_device* pDevice)
10083{
10084 MA_ASSERT(pDevice != NULL);
10085
10086#ifdef MA_WIN32_DESKTOP
10087 if (pDevice->wasapi.pDeviceEnumerator) {
10088 ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient);
10089 ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator);
10090 }
10091#endif
10092
10093 if (pDevice->wasapi.pRenderClient) {
10094 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
10095 }
10096 if (pDevice->wasapi.pCaptureClient) {
10097 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
10098 }
10099
10100 if (pDevice->wasapi.pAudioClientPlayback) {
10101 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
10102 }
10103 if (pDevice->wasapi.pAudioClientCapture) {
10104 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
10105 }
10106
10107 if (pDevice->wasapi.hEventPlayback) {
10108 CloseHandle(pDevice->wasapi.hEventPlayback);
10109 }
10110 if (pDevice->wasapi.hEventCapture) {
10111 CloseHandle(pDevice->wasapi.hEventCapture);
10112 }
10113}
10114
10115
10116typedef struct
10117{
10118 /* Input. */
10119 ma_format formatIn;
10120 ma_uint32 channelsIn;
10121 ma_uint32 sampleRateIn;
10122 ma_channel channelMapIn[MA_MAX_CHANNELS];
10123 ma_uint32 periodSizeInFramesIn;
10124 ma_uint32 periodSizeInMillisecondsIn;
10125 ma_uint32 periodsIn;
10126 ma_bool32 usingDefaultFormat;
10127 ma_bool32 usingDefaultChannels;
10128 ma_bool32 usingDefaultSampleRate;
10129 ma_bool32 usingDefaultChannelMap;
10130 ma_share_mode shareMode;
10131 ma_bool32 noAutoConvertSRC;
10132 ma_bool32 noDefaultQualitySRC;
10133 ma_bool32 noHardwareOffloading;
10134
10135 /* Output. */
10136 ma_IAudioClient* pAudioClient;
10137 ma_IAudioRenderClient* pRenderClient;
10138 ma_IAudioCaptureClient* pCaptureClient;
10139 ma_format formatOut;
10140 ma_uint32 channelsOut;
10141 ma_uint32 sampleRateOut;
10142 ma_channel channelMapOut[MA_MAX_CHANNELS];
10143 ma_uint32 periodSizeInFramesOut;
10144 ma_uint32 periodsOut;
10145 ma_bool32 usingAudioClient3;
10146 char deviceName[256];
10147} ma_device_init_internal_data__wasapi;
10148
10149static 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)
10150{
10151 HRESULT hr;
10152 ma_result result = MA_SUCCESS;
10153 const char* errorMsg = "";
10154 MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
10155 DWORD streamFlags = 0;
10156 MA_REFERENCE_TIME periodDurationInMicroseconds;
10157 ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE;
10158 WAVEFORMATEXTENSIBLE wf = {0};
10159 ma_WASAPIDeviceInterface* pDeviceInterface = NULL;
10160 ma_IAudioClient2* pAudioClient2;
10161 ma_uint32 nativeSampleRate;
10162
10163 MA_ASSERT(pContext != NULL);
10164 MA_ASSERT(pData != NULL);
10165
10166 /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */
10167 if (deviceType == ma_device_type_duplex) {
10168 return MA_INVALID_ARGS;
10169 }
10170
10171 pData->pAudioClient = NULL;
10172 pData->pRenderClient = NULL;
10173 pData->pCaptureClient = NULL;
10174
10175 streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
10176 if (!pData->noAutoConvertSRC && !pData->usingDefaultSampleRate && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */
10177 streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
10178 }
10179 if (!pData->noDefaultQualitySRC && !pData->usingDefaultSampleRate && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) {
10180 streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
10181 }
10182 if (deviceType == ma_device_type_loopback) {
10183 streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK;
10184 }
10185
10186 result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, &pData->pAudioClient, &pDeviceInterface);
10187 if (result != MA_SUCCESS) {
10188 goto done;
10189 }
10190
10191
10192 /* Try enabling hardware offloading. */
10193 if (!pData->noHardwareOffloading) {
10194 hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2);
10195 if (SUCCEEDED(hr)) {
10196 BOOL isHardwareOffloadingSupported = 0;
10197 hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported);
10198 if (SUCCEEDED(hr) && isHardwareOffloadingSupported) {
10199 ma_AudioClientProperties clientProperties;
10200 MA_ZERO_OBJECT(&clientProperties);
10201 clientProperties.cbSize = sizeof(clientProperties);
10202 clientProperties.bIsOffload = 1;
10203 clientProperties.eCategory = MA_AudioCategory_Other;
10204 ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties);
10205 }
10206
10207 pAudioClient2->lpVtbl->Release(pAudioClient2);
10208 }
10209 }
10210
10211 /* 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. */
10212 result = MA_FORMAT_NOT_SUPPORTED;
10213 if (pData->shareMode == ma_share_mode_exclusive) {
10214 #ifdef MA_WIN32_DESKTOP
10215 /* In exclusive mode on desktop we always use the backend's native format. */
10216 ma_IPropertyStore* pStore = NULL;
10217 hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore);
10218 if (SUCCEEDED(hr)) {
10219 PROPVARIANT prop;
10220 ma_PropVariantInit(&prop);
10221 hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop);
10222 if (SUCCEEDED(hr)) {
10223 WAVEFORMATEX* pActualFormat = (WAVEFORMATEX*)prop.blob.pBlobData;
10224 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL);
10225 if (SUCCEEDED(hr)) {
10226 MA_COPY_MEMORY(&wf, pActualFormat, sizeof(WAVEFORMATEXTENSIBLE));
10227 }
10228
10229 ma_PropVariantClear(pContext, &prop);
10230 }
10231
10232 ma_IPropertyStore_Release(pStore);
10233 }
10234 #else
10235 /*
10236 I do not know how to query the device's native format on UWP so for now I'm just disabling support for
10237 exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported()
10238 until you find one that works.
10239
10240 TODO: Add support for exclusive mode to UWP.
10241 */
10242 hr = S_FALSE;
10243 #endif
10244
10245 if (hr == S_OK) {
10246 shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE;
10247 result = MA_SUCCESS;
10248 } else {
10249 result = MA_SHARE_MODE_NOT_SUPPORTED;
10250 }
10251 } else {
10252 /* In shared mode we are always using the format reported by the operating system. */
10253 WAVEFORMATEXTENSIBLE* pNativeFormat = NULL;
10254 hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (WAVEFORMATEX**)&pNativeFormat);
10255 if (hr != S_OK) {
10256 result = MA_FORMAT_NOT_SUPPORTED;
10257 } else {
10258 MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(wf));
10259 result = MA_SUCCESS;
10260 }
10261
10262 ma_CoTaskMemFree(pContext, pNativeFormat);
10263
10264 shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
10265 }
10266
10267 /* Return an error if we still haven't found a format. */
10268 if (result != MA_SUCCESS) {
10269 errorMsg = "[WASAPI] Failed to find best device mix format.";
10270 goto done;
10271 }
10272
10273 /*
10274 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
10275 WASAPI to perform the sample rate conversion.
10276 */
10277 nativeSampleRate = wf.Format.nSamplesPerSec;
10278 if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) {
10279 wf.Format.nSamplesPerSec = pData->sampleRateIn;
10280 wf.Format.nAvgBytesPerSec = wf.Format.nSamplesPerSec * wf.Format.nBlockAlign;
10281 }
10282
10283 pData->formatOut = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)&wf);
10284 pData->channelsOut = wf.Format.nChannels;
10285 pData->sampleRateOut = wf.Format.nSamplesPerSec;
10286
10287 /* Get the internal channel map based on the channel mask. */
10288 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut);
10289
10290 /* Period size. */
10291 pData->periodsOut = pData->periodsIn;
10292 pData->periodSizeInFramesOut = pData->periodSizeInFramesIn;
10293 if (pData->periodSizeInFramesOut == 0) {
10294 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.Format.nSamplesPerSec);
10295 }
10296
10297 periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.Format.nSamplesPerSec;
10298
10299
10300 /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */
10301 if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
10302 MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * 10;
10303
10304 /*
10305 If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing
10306 it and trying it again.
10307 */
10308 hr = E_FAIL;
10309 for (;;) {
10310 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL);
10311 if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) {
10312 if (bufferDuration > 500*10000) {
10313 break;
10314 } else {
10315 if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */
10316 break;
10317 }
10318
10319 bufferDuration = bufferDuration * 2;
10320 continue;
10321 }
10322 } else {
10323 break;
10324 }
10325 }
10326
10327 if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
10328 ma_uint32 bufferSizeInFrames;
10329 hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
10330 if (SUCCEEDED(hr)) {
10331 bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.Format.nSamplesPerSec * bufferSizeInFrames) + 0.5);
10332
10333 /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */
10334 ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
10335
10336 #ifdef MA_WIN32_DESKTOP
10337 hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient);
10338 #else
10339 hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient);
10340 #endif
10341
10342 if (SUCCEEDED(hr)) {
10343 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL);
10344 }
10345 }
10346 }
10347
10348 if (FAILED(hr)) {
10349 /* 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. */
10350 if (hr == E_ACCESSDENIED) {
10351 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED;
10352 } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
10353 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_DEVICE_BUSY;
10354 } else {
10355 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = MA_SHARE_MODE_NOT_SUPPORTED;
10356 }
10357 goto done;
10358 }
10359 }
10360
10361 if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) {
10362 /*
10363 Low latency shared mode via IAudioClient3.
10364
10365 NOTE
10366 ====
10367 Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the
10368 use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using
10369 any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to
10370 that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM.
10371 */
10372#ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE
10373 if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.Format.nSamplesPerSec) {
10374 ma_IAudioClient3* pAudioClient3 = NULL;
10375 hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3);
10376 if (SUCCEEDED(hr)) {
10377 UINT32 defaultPeriodInFrames;
10378 UINT32 fundamentalPeriodInFrames;
10379 UINT32 minPeriodInFrames;
10380 UINT32 maxPeriodInFrames;
10381 hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames);
10382 if (SUCCEEDED(hr)) {
10383 UINT32 desiredPeriodInFrames = pData->periodSizeInFramesOut;
10384 UINT32 actualPeriodInFrames = desiredPeriodInFrames;
10385
10386 /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */
10387 actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames;
10388 actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames;
10389
10390 /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */
10391 actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames);
10392
10393 #if defined(MA_DEBUG_OUTPUT)
10394 printf("[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames);
10395 printf(" defaultPeriodInFrames=%d\n", defaultPeriodInFrames);
10396 printf(" fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames);
10397 printf(" minPeriodInFrames=%d\n", minPeriodInFrames);
10398 printf(" maxPeriodInFrames=%d\n", maxPeriodInFrames);
10399 #endif
10400
10401 /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */
10402 if (actualPeriodInFrames >= desiredPeriodInFrames) {
10403 /*
10404 MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified,
10405 IAudioClient3_InitializeSharedAudioStream() will fail.
10406 */
10407 hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (WAVEFORMATEX*)&wf, NULL);
10408 if (SUCCEEDED(hr)) {
10409 wasInitializedUsingIAudioClient3 = MA_TRUE;
10410 pData->periodSizeInFramesOut = actualPeriodInFrames;
10411 #if defined(MA_DEBUG_OUTPUT)
10412 printf("[WASAPI] Using IAudioClient3\n");
10413 printf(" periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut);
10414 #endif
10415 } else {
10416 #if defined(MA_DEBUG_OUTPUT)
10417 printf("[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n");
10418 #endif
10419 }
10420 } else {
10421 #if defined(MA_DEBUG_OUTPUT)
10422 printf("[WASAPI] Not using IAudioClient3 because the desired period size is larger than the maximum supported by IAudioClient3.\n");
10423 #endif
10424 }
10425 } else {
10426 #if defined(MA_DEBUG_OUTPUT)
10427 printf("[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n");
10428 #endif
10429 }
10430
10431 ma_IAudioClient3_Release(pAudioClient3);
10432 pAudioClient3 = NULL;
10433 }
10434 }
10435#else
10436 #if defined(MA_DEBUG_OUTPUT)
10437 printf("[WASAPI] Not using IAudioClient3 because MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE is enabled.\n");
10438 #endif
10439#endif
10440
10441 /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */
10442 if (!wasInitializedUsingIAudioClient3) {
10443 MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */
10444 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL);
10445 if (FAILED(hr)) {
10446 if (hr == E_ACCESSDENIED) {
10447 errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED;
10448 } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
10449 errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_DEVICE_BUSY;
10450 } else {
10451 errorMsg = "[WASAPI] Failed to initialize device.", result = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
10452 }
10453
10454 goto done;
10455 }
10456 }
10457 }
10458
10459 if (!wasInitializedUsingIAudioClient3) {
10460 ma_uint32 bufferSizeInFrames;
10461 hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
10462 if (FAILED(hr)) {
10463 errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
10464 goto done;
10465 }
10466
10467 pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut;
10468 }
10469
10470 pData->usingAudioClient3 = wasInitializedUsingIAudioClient3;
10471
10472 if (deviceType == ma_device_type_playback) {
10473 hr = ma_IAudioClient_GetService((ma_IAudioClient*)pData->pAudioClient, &MA_IID_IAudioRenderClient, (void**)&pData->pRenderClient);
10474 } else {
10475 hr = ma_IAudioClient_GetService((ma_IAudioClient*)pData->pAudioClient, &MA_IID_IAudioCaptureClient, (void**)&pData->pCaptureClient);
10476 }
10477
10478 if (FAILED(hr)) {
10479 errorMsg = "[WASAPI] Failed to get audio client service.", result = MA_API_NOT_FOUND;
10480 goto done;
10481 }
10482
10483
10484 /* Grab the name of the device. */
10485#ifdef MA_WIN32_DESKTOP
10486 {
10487 ma_IPropertyStore *pProperties;
10488 hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties);
10489 if (SUCCEEDED(hr)) {
10490 PROPVARIANT varName;
10491 ma_PropVariantInit(&varName);
10492 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName);
10493 if (SUCCEEDED(hr)) {
10494 WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE);
10495 ma_PropVariantClear(pContext, &varName);
10496 }
10497
10498 ma_IPropertyStore_Release(pProperties);
10499 }
10500 }
10501#endif
10502
10503done:
10504 /* Clean up. */
10505#ifdef MA_WIN32_DESKTOP
10506 if (pDeviceInterface != NULL) {
10507 ma_IMMDevice_Release(pDeviceInterface);
10508 }
10509#else
10510 if (pDeviceInterface != NULL) {
10511 ma_IUnknown_Release(pDeviceInterface);
10512 }
10513#endif
10514
10515 if (result != MA_SUCCESS) {
10516 if (pData->pRenderClient) {
10517 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient);
10518 pData->pRenderClient = NULL;
10519 }
10520 if (pData->pCaptureClient) {
10521 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient);
10522 pData->pCaptureClient = NULL;
10523 }
10524 if (pData->pAudioClient) {
10525 ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
10526 pData->pAudioClient = NULL;
10527 }
10528
10529 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, errorMsg, result);
10530 } else {
10531 return MA_SUCCESS;
10532 }
10533}
10534
10535static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType)
10536{
10537 ma_device_init_internal_data__wasapi data;
10538 ma_result result;
10539
10540 MA_ASSERT(pDevice != NULL);
10541
10542 /* We only re-initialize the playback or capture device. Never a full-duplex device. */
10543 if (deviceType == ma_device_type_duplex) {
10544 return MA_INVALID_ARGS;
10545 }
10546
10547 if (deviceType == ma_device_type_playback) {
10548 data.formatIn = pDevice->playback.format;
10549 data.channelsIn = pDevice->playback.channels;
10550 MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
10551 data.shareMode = pDevice->playback.shareMode;
10552 data.usingDefaultFormat = pDevice->playback.usingDefaultFormat;
10553 data.usingDefaultChannels = pDevice->playback.usingDefaultChannels;
10554 data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap;
10555 } else {
10556 data.formatIn = pDevice->capture.format;
10557 data.channelsIn = pDevice->capture.channels;
10558 MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
10559 data.shareMode = pDevice->capture.shareMode;
10560 data.usingDefaultFormat = pDevice->capture.usingDefaultFormat;
10561 data.usingDefaultChannels = pDevice->capture.usingDefaultChannels;
10562 data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap;
10563 }
10564
10565 data.sampleRateIn = pDevice->sampleRate;
10566 data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
10567 data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames;
10568 data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds;
10569 data.periodsIn = pDevice->wasapi.originalPeriods;
10570 data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC;
10571 data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC;
10572 data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading;
10573 result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data);
10574 if (result != MA_SUCCESS) {
10575 return result;
10576 }
10577
10578 /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */
10579 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
10580 if (pDevice->wasapi.pCaptureClient) {
10581 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
10582 pDevice->wasapi.pCaptureClient = NULL;
10583 }
10584
10585 if (pDevice->wasapi.pAudioClientCapture) {
10586 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
10587 pDevice->wasapi.pAudioClientCapture = NULL;
10588 }
10589
10590 pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
10591 pDevice->wasapi.pCaptureClient = data.pCaptureClient;
10592
10593 pDevice->capture.internalFormat = data.formatOut;
10594 pDevice->capture.internalChannels = data.channelsOut;
10595 pDevice->capture.internalSampleRate = data.sampleRateOut;
10596 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
10597 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
10598 pDevice->capture.internalPeriods = data.periodsOut;
10599 ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName);
10600
10601 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture);
10602
10603 pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
10604 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualPeriodSizeInFramesCapture);
10605
10606 /* The device may be in a started state. If so we need to immediately restart it. */
10607 if (pDevice->wasapi.isStartedCapture) {
10608 HRESULT hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
10609 if (FAILED(hr)) {
10610 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device after reinitialization.", MA_FAILED_TO_START_BACKEND_DEVICE);
10611 }
10612 }
10613 }
10614
10615 if (deviceType == ma_device_type_playback) {
10616 if (pDevice->wasapi.pRenderClient) {
10617 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
10618 pDevice->wasapi.pRenderClient = NULL;
10619 }
10620
10621 if (pDevice->wasapi.pAudioClientPlayback) {
10622 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
10623 pDevice->wasapi.pAudioClientPlayback = NULL;
10624 }
10625
10626 pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
10627 pDevice->wasapi.pRenderClient = data.pRenderClient;
10628
10629 pDevice->playback.internalFormat = data.formatOut;
10630 pDevice->playback.internalChannels = data.channelsOut;
10631 pDevice->playback.internalSampleRate = data.sampleRateOut;
10632 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
10633 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
10634 pDevice->playback.internalPeriods = data.periodsOut;
10635 ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName);
10636
10637 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback);
10638
10639 pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
10640 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualPeriodSizeInFramesPlayback);
10641
10642 /* The device may be in a started state. If so we need to immediately restart it. */
10643 if (pDevice->wasapi.isStartedPlayback) {
10644 HRESULT hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
10645 if (FAILED(hr)) {
10646 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device after reinitialization.", MA_FAILED_TO_START_BACKEND_DEVICE);
10647 }
10648 }
10649 }
10650
10651 return MA_SUCCESS;
10652}
10653
10654static ma_result ma_device_init__wasapi(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
10655{
10656 ma_result result = MA_SUCCESS;
10657
10658 (void)pContext;
10659
10660 MA_ASSERT(pContext != NULL);
10661 MA_ASSERT(pDevice != NULL);
10662
10663 MA_ZERO_OBJECT(&pDevice->wasapi);
10664 pDevice->wasapi.originalPeriodSizeInFrames = pConfig->periodSizeInFrames;
10665 pDevice->wasapi.originalPeriodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
10666 pDevice->wasapi.originalPeriods = pConfig->periods;
10667 pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
10668 pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
10669 pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
10670
10671 /* Exclusive mode is not allowed with loopback. */
10672 if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) {
10673 return MA_INVALID_DEVICE_CONFIG;
10674 }
10675
10676 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
10677 ma_device_init_internal_data__wasapi data;
10678 data.formatIn = pConfig->capture.format;
10679 data.channelsIn = pConfig->capture.channels;
10680 data.sampleRateIn = pConfig->sampleRate;
10681 MA_COPY_MEMORY(data.channelMapIn, pConfig->capture.channelMap, sizeof(pConfig->capture.channelMap));
10682 data.usingDefaultFormat = pDevice->capture.usingDefaultFormat;
10683 data.usingDefaultChannels = pDevice->capture.usingDefaultChannels;
10684 data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
10685 data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap;
10686 data.shareMode = pConfig->capture.shareMode;
10687 data.periodSizeInFramesIn = pConfig->periodSizeInFrames;
10688 data.periodSizeInMillisecondsIn = pConfig->periodSizeInMilliseconds;
10689 data.periodsIn = pConfig->periods;
10690 data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
10691 data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
10692 data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
10693
10694 result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pConfig->capture.pDeviceID, &data);
10695 if (result != MA_SUCCESS) {
10696 return result;
10697 }
10698
10699 pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
10700 pDevice->wasapi.pCaptureClient = data.pCaptureClient;
10701
10702 pDevice->capture.internalFormat = data.formatOut;
10703 pDevice->capture.internalChannels = data.channelsOut;
10704 pDevice->capture.internalSampleRate = data.sampleRateOut;
10705 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
10706 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
10707 pDevice->capture.internalPeriods = data.periodsOut;
10708 ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName);
10709
10710 /*
10711 The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled,
10712 however, because we want to block until we actually have something for the first call to ma_device_read().
10713 */
10714 pDevice->wasapi.hEventCapture = CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */
10715 if (pDevice->wasapi.hEventCapture == NULL) {
10716 if (pDevice->wasapi.pCaptureClient != NULL) {
10717 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
10718 pDevice->wasapi.pCaptureClient = NULL;
10719 }
10720 if (pDevice->wasapi.pAudioClientCapture != NULL) {
10721 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
10722 pDevice->wasapi.pAudioClientCapture = NULL;
10723 }
10724
10725 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture.", MA_FAILED_TO_CREATE_EVENT);
10726 }
10727 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture);
10728
10729 pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
10730 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualPeriodSizeInFramesCapture);
10731 }
10732
10733 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
10734 ma_device_init_internal_data__wasapi data;
10735 data.formatIn = pConfig->playback.format;
10736 data.channelsIn = pConfig->playback.channels;
10737 data.sampleRateIn = pConfig->sampleRate;
10738 MA_COPY_MEMORY(data.channelMapIn, pConfig->playback.channelMap, sizeof(pConfig->playback.channelMap));
10739 data.usingDefaultFormat = pDevice->playback.usingDefaultFormat;
10740 data.usingDefaultChannels = pDevice->playback.usingDefaultChannels;
10741 data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
10742 data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap;
10743 data.shareMode = pConfig->playback.shareMode;
10744 data.periodSizeInFramesIn = pConfig->periodSizeInFrames;
10745 data.periodSizeInMillisecondsIn = pConfig->periodSizeInMilliseconds;
10746 data.periodsIn = pConfig->periods;
10747 data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
10748 data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
10749 data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
10750
10751 result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pConfig->playback.pDeviceID, &data);
10752 if (result != MA_SUCCESS) {
10753 if (pConfig->deviceType == ma_device_type_duplex) {
10754 if (pDevice->wasapi.pCaptureClient != NULL) {
10755 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
10756 pDevice->wasapi.pCaptureClient = NULL;
10757 }
10758 if (pDevice->wasapi.pAudioClientCapture != NULL) {
10759 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
10760 pDevice->wasapi.pAudioClientCapture = NULL;
10761 }
10762
10763 CloseHandle(pDevice->wasapi.hEventCapture);
10764 pDevice->wasapi.hEventCapture = NULL;
10765 }
10766 return result;
10767 }
10768
10769 pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
10770 pDevice->wasapi.pRenderClient = data.pRenderClient;
10771
10772 pDevice->playback.internalFormat = data.formatOut;
10773 pDevice->playback.internalChannels = data.channelsOut;
10774 pDevice->playback.internalSampleRate = data.sampleRateOut;
10775 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
10776 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
10777 pDevice->playback.internalPeriods = data.periodsOut;
10778 ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName);
10779
10780 /*
10781 The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled
10782 only after the whole available space has been filled, never before.
10783
10784 The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able
10785 to get passed WaitForMultipleObjects().
10786 */
10787 pDevice->wasapi.hEventPlayback = CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */
10788 if (pDevice->wasapi.hEventPlayback == NULL) {
10789 if (pConfig->deviceType == ma_device_type_duplex) {
10790 if (pDevice->wasapi.pCaptureClient != NULL) {
10791 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
10792 pDevice->wasapi.pCaptureClient = NULL;
10793 }
10794 if (pDevice->wasapi.pAudioClientCapture != NULL) {
10795 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
10796 pDevice->wasapi.pAudioClientCapture = NULL;
10797 }
10798
10799 CloseHandle(pDevice->wasapi.hEventCapture);
10800 pDevice->wasapi.hEventCapture = NULL;
10801 }
10802
10803 if (pDevice->wasapi.pRenderClient != NULL) {
10804 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
10805 pDevice->wasapi.pRenderClient = NULL;
10806 }
10807 if (pDevice->wasapi.pAudioClientPlayback != NULL) {
10808 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
10809 pDevice->wasapi.pAudioClientPlayback = NULL;
10810 }
10811
10812 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback.", MA_FAILED_TO_CREATE_EVENT);
10813 }
10814 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback);
10815
10816 pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
10817 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualPeriodSizeInFramesPlayback);
10818 }
10819
10820 /*
10821 We need to get notifications of when the default device changes. We do this through a device enumerator by
10822 registering a IMMNotificationClient with it. We only care about this if it's the default device.
10823 */
10824#ifdef MA_WIN32_DESKTOP
10825 if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) {
10826 if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID == NULL) {
10827 pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE;
10828 }
10829 if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) {
10830 pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE;
10831 }
10832
10833 if (pDevice->wasapi.allowCaptureAutoStreamRouting || pDevice->wasapi.allowPlaybackAutoStreamRouting) {
10834 ma_IMMDeviceEnumerator* pDeviceEnumerator;
10835 HRESULT hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
10836 if (FAILED(hr)) {
10837 ma_device_uninit__wasapi(pDevice);
10838 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
10839 }
10840
10841 pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl;
10842 pDevice->wasapi.notificationClient.counter = 1;
10843 pDevice->wasapi.notificationClient.pDevice = pDevice;
10844
10845 hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient);
10846 if (SUCCEEDED(hr)) {
10847 pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator;
10848 } else {
10849 /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */
10850 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
10851 }
10852 }
10853 }
10854#endif
10855
10856 ma_atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE);
10857 ma_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
10858
10859 return MA_SUCCESS;
10860}
10861
10862static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount)
10863{
10864 ma_uint32 paddingFramesCount;
10865 HRESULT hr;
10866 ma_share_mode shareMode;
10867
10868 MA_ASSERT(pDevice != NULL);
10869 MA_ASSERT(pFrameCount != NULL);
10870
10871 *pFrameCount = 0;
10872
10873 if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) {
10874 return MA_INVALID_OPERATION;
10875 }
10876
10877 hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount);
10878 if (FAILED(hr)) {
10879 return MA_DEVICE_UNAVAILABLE;
10880 }
10881
10882 /* Slightly different rules for exclusive and shared modes. */
10883 shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode;
10884 if (shareMode == ma_share_mode_exclusive) {
10885 *pFrameCount = paddingFramesCount;
10886 } else {
10887 if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
10888 *pFrameCount = pDevice->wasapi.actualPeriodSizeInFramesPlayback - paddingFramesCount;
10889 } else {
10890 *pFrameCount = paddingFramesCount;
10891 }
10892 }
10893
10894 return MA_SUCCESS;
10895}
10896
10897static ma_bool32 ma_device_is_reroute_required__wasapi(ma_device* pDevice, ma_device_type deviceType)
10898{
10899 MA_ASSERT(pDevice != NULL);
10900
10901 if (deviceType == ma_device_type_playback) {
10902 return pDevice->wasapi.hasDefaultPlaybackDeviceChanged;
10903 }
10904
10905 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
10906 return pDevice->wasapi.hasDefaultCaptureDeviceChanged;
10907 }
10908
10909 return MA_FALSE;
10910}
10911
10912static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType)
10913{
10914 ma_result result;
10915
10916 if (deviceType == ma_device_type_duplex) {
10917 return MA_INVALID_ARGS;
10918 }
10919
10920 if (deviceType == ma_device_type_playback) {
10921 ma_atomic_exchange_32(&pDevice->wasapi.hasDefaultPlaybackDeviceChanged, MA_FALSE);
10922 }
10923 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
10924 ma_atomic_exchange_32(&pDevice->wasapi.hasDefaultCaptureDeviceChanged, MA_FALSE);
10925 }
10926
10927
10928 #ifdef MA_DEBUG_OUTPUT
10929 printf("=== CHANGING DEVICE ===\n");
10930 #endif
10931
10932 result = ma_device_reinit__wasapi(pDevice, deviceType);
10933 if (result != MA_SUCCESS) {
10934 return result;
10935 }
10936
10937 ma_device__post_init_setup(pDevice, deviceType);
10938
10939 return MA_SUCCESS;
10940}
10941
10942
10943static ma_result ma_device_stop__wasapi(ma_device* pDevice)
10944{
10945 MA_ASSERT(pDevice != NULL);
10946
10947 /*
10948 We need to explicitly signal the capture event in loopback mode to ensure we return from WaitForSingleObject() when nothing is being played. When nothing
10949 is being played, the event is never signalled internally by WASAPI which means we will deadlock when stopping the device.
10950 */
10951 if (pDevice->type == ma_device_type_loopback) {
10952 SetEvent((HANDLE)pDevice->wasapi.hEventCapture);
10953 }
10954
10955 return MA_SUCCESS;
10956}
10957
10958
10959static ma_result ma_device_main_loop__wasapi(ma_device* pDevice)
10960{
10961 ma_result result;
10962 HRESULT hr;
10963 ma_bool32 exitLoop = MA_FALSE;
10964 ma_uint32 framesWrittenToPlaybackDevice = 0;
10965 ma_uint32 mappedDeviceBufferSizeInFramesCapture = 0;
10966 ma_uint32 mappedDeviceBufferSizeInFramesPlayback = 0;
10967 ma_uint32 mappedDeviceBufferFramesRemainingCapture = 0;
10968 ma_uint32 mappedDeviceBufferFramesRemainingPlayback = 0;
10969 BYTE* pMappedDeviceBufferCapture = NULL;
10970 BYTE* pMappedDeviceBufferPlayback = NULL;
10971 ma_uint32 bpfCaptureDevice = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
10972 ma_uint32 bpfPlaybackDevice = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
10973 ma_uint32 bpfCaptureClient = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
10974 ma_uint32 bpfPlaybackClient = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
10975 ma_uint8 inputDataInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
10976 ma_uint32 inputDataInClientFormatCap = sizeof(inputDataInClientFormat) / bpfCaptureClient;
10977 ma_uint8 outputDataInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
10978 ma_uint32 outputDataInClientFormatCap = sizeof(outputDataInClientFormat) / bpfPlaybackClient;
10979 ma_uint32 outputDataInClientFormatCount = 0;
10980 ma_uint32 outputDataInClientFormatConsumed = 0;
10981 ma_uint32 periodSizeInFramesCapture = 0;
10982
10983 MA_ASSERT(pDevice != NULL);
10984
10985 /* The capture device needs to be started immediately. */
10986 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
10987 periodSizeInFramesCapture = pDevice->capture.internalPeriodSizeInFrames;
10988
10989 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
10990 if (FAILED(hr)) {
10991 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device.", MA_FAILED_TO_START_BACKEND_DEVICE);
10992 }
10993 ma_atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_TRUE);
10994 }
10995
10996 while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
10997 /* We may need to reroute the device. */
10998 if (ma_device_is_reroute_required__wasapi(pDevice, ma_device_type_playback)) {
10999 result = ma_device_reroute__wasapi(pDevice, ma_device_type_playback);
11000 if (result != MA_SUCCESS) {
11001 exitLoop = MA_TRUE;
11002 break;
11003 }
11004 }
11005 if (ma_device_is_reroute_required__wasapi(pDevice, ma_device_type_capture)) {
11006 result = ma_device_reroute__wasapi(pDevice, (pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);
11007 if (result != MA_SUCCESS) {
11008 exitLoop = MA_TRUE;
11009 break;
11010 }
11011 }
11012
11013 switch (pDevice->type)
11014 {
11015 case ma_device_type_duplex:
11016 {
11017 ma_uint32 framesAvailableCapture;
11018 ma_uint32 framesAvailablePlayback;
11019 DWORD flagsCapture; /* Passed to IAudioCaptureClient_GetBuffer(). */
11020
11021 /* The process is to map the playback buffer and fill it as quickly as possible from input data. */
11022 if (pMappedDeviceBufferPlayback == NULL) {
11023 /* WASAPI is weird with exclusive mode. You need to wait on the event _before_ querying the available frames. */
11024 if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
11025 if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) {
11026 return MA_ERROR; /* Wait failed. */
11027 }
11028 }
11029
11030 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
11031 if (result != MA_SUCCESS) {
11032 return result;
11033 }
11034
11035 /*printf("TRACE 1: framesAvailablePlayback=%d\n", framesAvailablePlayback);*/
11036
11037
11038 /* In exclusive mode, the frame count needs to exactly match the value returned by GetCurrentPadding(). */
11039 if (pDevice->playback.shareMode != ma_share_mode_exclusive) {
11040 if (framesAvailablePlayback > pDevice->wasapi.periodSizeInFramesPlayback) {
11041 framesAvailablePlayback = pDevice->wasapi.periodSizeInFramesPlayback;
11042 }
11043 }
11044
11045 /* If there's no frames available in the playback device we need to wait for more. */
11046 if (framesAvailablePlayback == 0) {
11047 /* In exclusive mode we waited at the top. */
11048 if (pDevice->playback.shareMode != ma_share_mode_exclusive) {
11049 if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) {
11050 return MA_ERROR; /* Wait failed. */
11051 }
11052 }
11053
11054 continue;
11055 }
11056
11057 /* We're ready to map the playback device's buffer. We don't release this until it's been entirely filled. */
11058 hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, &pMappedDeviceBufferPlayback);
11059 if (FAILED(hr)) {
11060 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_FAILED_TO_MAP_DEVICE_BUFFER);
11061 exitLoop = MA_TRUE;
11062 break;
11063 }
11064
11065 mappedDeviceBufferSizeInFramesPlayback = framesAvailablePlayback;
11066 mappedDeviceBufferFramesRemainingPlayback = framesAvailablePlayback;
11067 }
11068
11069 /* At this point we should have a buffer available for output. We need to keep writing input samples to it. */
11070 for (;;) {
11071 /* Try grabbing some captured data if we haven't already got a mapped buffer. */
11072 if (pMappedDeviceBufferCapture == NULL) {
11073 if (pDevice->capture.shareMode == ma_share_mode_shared) {
11074 if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) {
11075 return MA_ERROR; /* Wait failed. */
11076 }
11077 }
11078
11079 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture);
11080 if (result != MA_SUCCESS) {
11081 exitLoop = MA_TRUE;
11082 break;
11083 }
11084
11085 /*printf("TRACE 2: framesAvailableCapture=%d\n", framesAvailableCapture);*/
11086
11087 /* Wait for more if nothing is available. */
11088 if (framesAvailableCapture == 0) {
11089 /* In exclusive mode we waited at the top. */
11090 if (pDevice->capture.shareMode != ma_share_mode_shared) {
11091 if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) {
11092 return MA_ERROR; /* Wait failed. */
11093 }
11094 }
11095
11096 continue;
11097 }
11098
11099 /* Getting here means there's data available for writing to the output device. */
11100 mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture);
11101 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
11102 if (FAILED(hr)) {
11103 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_FAILED_TO_MAP_DEVICE_BUFFER);
11104 exitLoop = MA_TRUE;
11105 break;
11106 }
11107
11108
11109 /* Overrun detection. */
11110 if ((flagsCapture & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
11111 /* Glitched. Probably due to an overrun. */
11112 #ifdef MA_DEBUG_OUTPUT
11113 printf("[WASAPI] Data discontinuity (possible overrun). framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
11114 #endif
11115
11116 /*
11117 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
11118 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
11119 last period.
11120 */
11121 if (framesAvailableCapture >= pDevice->wasapi.actualPeriodSizeInFramesCapture) {
11122 #ifdef MA_DEBUG_OUTPUT
11123 printf("[WASAPI] Synchronizing capture stream. ");
11124 #endif
11125 do
11126 {
11127 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
11128 if (FAILED(hr)) {
11129 break;
11130 }
11131
11132 framesAvailableCapture -= mappedDeviceBufferSizeInFramesCapture;
11133
11134 if (framesAvailableCapture > 0) {
11135 mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture);
11136 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
11137 if (FAILED(hr)) {
11138 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_FAILED_TO_MAP_DEVICE_BUFFER);
11139 exitLoop = MA_TRUE;
11140 break;
11141 }
11142 } else {
11143 pMappedDeviceBufferCapture = NULL;
11144 mappedDeviceBufferSizeInFramesCapture = 0;
11145 }
11146 } while (framesAvailableCapture > periodSizeInFramesCapture);
11147 #ifdef MA_DEBUG_OUTPUT
11148 printf("framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
11149 #endif
11150 }
11151 } else {
11152 #ifdef MA_DEBUG_OUTPUT
11153 if (flagsCapture != 0) {
11154 printf("[WASAPI] Capture Flags: %d\n", flagsCapture);
11155 }
11156 #endif
11157 }
11158
11159 mappedDeviceBufferFramesRemainingCapture = mappedDeviceBufferSizeInFramesCapture;
11160 }
11161
11162
11163 /* 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. */
11164 for (;;) {
11165 BYTE* pRunningDeviceBufferCapture;
11166 BYTE* pRunningDeviceBufferPlayback;
11167 ma_uint32 framesToProcess;
11168 ma_uint32 framesProcessed;
11169
11170 pRunningDeviceBufferCapture = pMappedDeviceBufferCapture + ((mappedDeviceBufferSizeInFramesCapture - mappedDeviceBufferFramesRemainingCapture ) * bpfCaptureDevice);
11171 pRunningDeviceBufferPlayback = pMappedDeviceBufferPlayback + ((mappedDeviceBufferSizeInFramesPlayback - mappedDeviceBufferFramesRemainingPlayback) * bpfPlaybackDevice);
11172
11173 /* There may be some data sitting in the converter that needs to be processed first. Once this is exhaused, run the data callback again. */
11174 if (!pDevice->playback.converter.isPassthrough && outputDataInClientFormatConsumed < outputDataInClientFormatCount) {
11175 ma_uint64 convertedFrameCountClient = (outputDataInClientFormatCount - outputDataInClientFormatConsumed);
11176 ma_uint64 convertedFrameCountDevice = mappedDeviceBufferFramesRemainingPlayback;
11177 void* pConvertedFramesClient = outputDataInClientFormat + (outputDataInClientFormatConsumed * bpfPlaybackClient);
11178 void* pConvertedFramesDevice = pRunningDeviceBufferPlayback;
11179 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesClient, &convertedFrameCountClient, pConvertedFramesDevice, &convertedFrameCountDevice);
11180 if (result != MA_SUCCESS) {
11181 break;
11182 }
11183
11184 outputDataInClientFormatConsumed += (ma_uint32)convertedFrameCountClient; /* Safe cast. */
11185 mappedDeviceBufferFramesRemainingPlayback -= (ma_uint32)convertedFrameCountDevice; /* Safe cast. */
11186
11187 if (mappedDeviceBufferFramesRemainingPlayback == 0) {
11188 break;
11189 }
11190 }
11191
11192 /*
11193 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
11194 buffers directly to the callback.
11195 */
11196 if (pDevice->capture.converter.isPassthrough && pDevice->playback.converter.isPassthrough) {
11197 /* Optimal path. We can pass mapped pointers directly to the callback. */
11198 framesToProcess = ma_min(mappedDeviceBufferFramesRemainingCapture, mappedDeviceBufferFramesRemainingPlayback);
11199 framesProcessed = framesToProcess;
11200
11201 ma_device__on_data(pDevice, pRunningDeviceBufferPlayback, pRunningDeviceBufferCapture, framesToProcess);
11202
11203 mappedDeviceBufferFramesRemainingCapture -= framesProcessed;
11204 mappedDeviceBufferFramesRemainingPlayback -= framesProcessed;
11205
11206 if (mappedDeviceBufferFramesRemainingCapture == 0) {
11207 break; /* Exhausted input data. */
11208 }
11209 if (mappedDeviceBufferFramesRemainingPlayback == 0) {
11210 break; /* Exhausted output data. */
11211 }
11212 } else if (pDevice->capture.converter.isPassthrough) {
11213 /* The input buffer is a passthrough, but the playback buffer requires a conversion. */
11214 framesToProcess = ma_min(mappedDeviceBufferFramesRemainingCapture, outputDataInClientFormatCap);
11215 framesProcessed = framesToProcess;
11216
11217 ma_device__on_data(pDevice, outputDataInClientFormat, pRunningDeviceBufferCapture, framesToProcess);
11218 outputDataInClientFormatCount = framesProcessed;
11219 outputDataInClientFormatConsumed = 0;
11220
11221 mappedDeviceBufferFramesRemainingCapture -= framesProcessed;
11222 if (mappedDeviceBufferFramesRemainingCapture == 0) {
11223 break; /* Exhausted input data. */
11224 }
11225 } else if (pDevice->playback.converter.isPassthrough) {
11226 /* The input buffer requires conversion, the playback buffer is passthrough. */
11227 ma_uint64 capturedDeviceFramesToProcess = mappedDeviceBufferFramesRemainingCapture;
11228 ma_uint64 capturedClientFramesToProcess = ma_min(inputDataInClientFormatCap, mappedDeviceBufferFramesRemainingPlayback);
11229
11230 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningDeviceBufferCapture, &capturedDeviceFramesToProcess, inputDataInClientFormat, &capturedClientFramesToProcess);
11231 if (result != MA_SUCCESS) {
11232 break;
11233 }
11234
11235 if (capturedClientFramesToProcess == 0) {
11236 break;
11237 }
11238
11239 ma_device__on_data(pDevice, pRunningDeviceBufferPlayback, inputDataInClientFormat, (ma_uint32)capturedClientFramesToProcess); /* Safe cast. */
11240
11241 mappedDeviceBufferFramesRemainingCapture -= (ma_uint32)capturedDeviceFramesToProcess;
11242 mappedDeviceBufferFramesRemainingPlayback -= (ma_uint32)capturedClientFramesToProcess;
11243 } else {
11244 ma_uint64 capturedDeviceFramesToProcess = mappedDeviceBufferFramesRemainingCapture;
11245 ma_uint64 capturedClientFramesToProcess = ma_min(inputDataInClientFormatCap, outputDataInClientFormatCap);
11246
11247 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningDeviceBufferCapture, &capturedDeviceFramesToProcess, inputDataInClientFormat, &capturedClientFramesToProcess);
11248 if (result != MA_SUCCESS) {
11249 break;
11250 }
11251
11252 if (capturedClientFramesToProcess == 0) {
11253 break;
11254 }
11255
11256 ma_device__on_data(pDevice, outputDataInClientFormat, inputDataInClientFormat, (ma_uint32)capturedClientFramesToProcess);
11257
11258 mappedDeviceBufferFramesRemainingCapture -= (ma_uint32)capturedDeviceFramesToProcess;
11259 outputDataInClientFormatCount = (ma_uint32)capturedClientFramesToProcess;
11260 outputDataInClientFormatConsumed = 0;
11261 }
11262 }
11263
11264
11265 /* If at this point we've run out of capture data we need to release the buffer. */
11266 if (mappedDeviceBufferFramesRemainingCapture == 0 && pMappedDeviceBufferCapture != NULL) {
11267 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
11268 if (FAILED(hr)) {
11269 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from capture device after reading from the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
11270 exitLoop = MA_TRUE;
11271 break;
11272 }
11273
11274 /*printf("TRACE: Released capture buffer\n");*/
11275
11276 pMappedDeviceBufferCapture = NULL;
11277 mappedDeviceBufferFramesRemainingCapture = 0;
11278 mappedDeviceBufferSizeInFramesCapture = 0;
11279 }
11280
11281 /* Get out of this loop if we're run out of room in the playback buffer. */
11282 if (mappedDeviceBufferFramesRemainingPlayback == 0) {
11283 break;
11284 }
11285 }
11286
11287
11288 /* If at this point we've run out of data we need to release the buffer. */
11289 if (mappedDeviceBufferFramesRemainingPlayback == 0 && pMappedDeviceBufferPlayback != NULL) {
11290 hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, mappedDeviceBufferSizeInFramesPlayback, 0);
11291 if (FAILED(hr)) {
11292 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from playback device after writing to the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
11293 exitLoop = MA_TRUE;
11294 break;
11295 }
11296
11297 /*printf("TRACE: Released playback buffer\n");*/
11298 framesWrittenToPlaybackDevice += mappedDeviceBufferSizeInFramesPlayback;
11299
11300 pMappedDeviceBufferPlayback = NULL;
11301 mappedDeviceBufferFramesRemainingPlayback = 0;
11302 mappedDeviceBufferSizeInFramesPlayback = 0;
11303 }
11304
11305 if (!pDevice->wasapi.isStartedPlayback) {
11306 ma_uint32 startThreshold = pDevice->playback.internalPeriodSizeInFrames * 1;
11307
11308 /* 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. */
11309 if (startThreshold > pDevice->wasapi.actualPeriodSizeInFramesPlayback) {
11310 startThreshold = pDevice->wasapi.actualPeriodSizeInFramesPlayback;
11311 }
11312
11313 if (pDevice->playback.shareMode == ma_share_mode_exclusive || framesWrittenToPlaybackDevice >= startThreshold) {
11314 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
11315 if (FAILED(hr)) {
11316 ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
11317 ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
11318 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", MA_FAILED_TO_START_BACKEND_DEVICE);
11319 }
11320 ma_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
11321 }
11322 }
11323 } break;
11324
11325
11326
11327 case ma_device_type_capture:
11328 case ma_device_type_loopback:
11329 {
11330 ma_uint32 framesAvailableCapture;
11331 DWORD flagsCapture; /* Passed to IAudioCaptureClient_GetBuffer(). */
11332
11333 /* Wait for data to become available first. */
11334 if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) {
11335 exitLoop = MA_TRUE;
11336 break; /* Wait failed. */
11337 }
11338
11339 /* 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. */
11340 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture);
11341 if (result != MA_SUCCESS) {
11342 exitLoop = MA_TRUE;
11343 break;
11344 }
11345
11346 if (framesAvailableCapture < pDevice->wasapi.periodSizeInFramesCapture) {
11347 continue; /* Nothing available. Keep waiting. */
11348 }
11349
11350 /* Map the data buffer in preparation for sending to the client. */
11351 mappedDeviceBufferSizeInFramesCapture = framesAvailableCapture;
11352 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
11353 if (FAILED(hr)) {
11354 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_FAILED_TO_MAP_DEVICE_BUFFER);
11355 exitLoop = MA_TRUE;
11356 break;
11357 }
11358
11359 /* We should have a buffer at this point. */
11360 ma_device__send_frames_to_client(pDevice, mappedDeviceBufferSizeInFramesCapture, pMappedDeviceBufferCapture);
11361
11362 /* At this point we're done with the buffer. */
11363 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
11364 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. */
11365 mappedDeviceBufferSizeInFramesCapture = 0;
11366 if (FAILED(hr)) {
11367 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from capture device after reading from the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
11368 exitLoop = MA_TRUE;
11369 break;
11370 }
11371 } break;
11372
11373
11374
11375 case ma_device_type_playback:
11376 {
11377 ma_uint32 framesAvailablePlayback;
11378
11379 /* Wait for space to become available first. */
11380 if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) {
11381 exitLoop = MA_TRUE;
11382 break; /* Wait failed. */
11383 }
11384
11385 /* Check how much space is available. If this returns 0 we just keep waiting. */
11386 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
11387 if (result != MA_SUCCESS) {
11388 exitLoop = MA_TRUE;
11389 break;
11390 }
11391
11392 if (framesAvailablePlayback < pDevice->wasapi.periodSizeInFramesPlayback) {
11393 continue; /* No space available. */
11394 }
11395
11396 /* Map a the data buffer in preparation for the callback. */
11397 hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, &pMappedDeviceBufferPlayback);
11398 if (FAILED(hr)) {
11399 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_FAILED_TO_MAP_DEVICE_BUFFER);
11400 exitLoop = MA_TRUE;
11401 break;
11402 }
11403
11404 /* We should have a buffer at this point. */
11405 ma_device__read_frames_from_client(pDevice, framesAvailablePlayback, pMappedDeviceBufferPlayback);
11406
11407 /* At this point we're done writing to the device and we just need to release the buffer. */
11408 hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, 0);
11409 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. */
11410 mappedDeviceBufferSizeInFramesPlayback = 0;
11411
11412 if (FAILED(hr)) {
11413 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from playback device after writing to the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
11414 exitLoop = MA_TRUE;
11415 break;
11416 }
11417
11418 framesWrittenToPlaybackDevice += framesAvailablePlayback;
11419 if (!pDevice->wasapi.isStartedPlayback) {
11420 if (pDevice->playback.shareMode == ma_share_mode_exclusive || framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames*1) {
11421 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
11422 if (FAILED(hr)) {
11423 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", MA_FAILED_TO_START_BACKEND_DEVICE);
11424 exitLoop = MA_TRUE;
11425 break;
11426 }
11427 ma_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
11428 }
11429 }
11430 } break;
11431
11432 default: return MA_INVALID_ARGS;
11433 }
11434 }
11435
11436 /* Here is where the device needs to be stopped. */
11437 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
11438 /* Any mapped buffers need to be released. */
11439 if (pMappedDeviceBufferCapture != NULL) {
11440 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
11441 }
11442
11443 hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
11444 if (FAILED(hr)) {
11445 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
11446 }
11447
11448 /* The audio client needs to be reset otherwise restarting will fail. */
11449 hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
11450 if (FAILED(hr)) {
11451 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
11452 }
11453
11454 ma_atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE);
11455 }
11456
11457 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
11458 /* Any mapped buffers need to be released. */
11459 if (pMappedDeviceBufferPlayback != NULL) {
11460 hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, mappedDeviceBufferSizeInFramesPlayback, 0);
11461 }
11462
11463 /*
11464 The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to
11465 the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played.
11466 */
11467 if (pDevice->wasapi.isStartedPlayback) {
11468 if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
11469 WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE);
11470 } else {
11471 ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1;
11472 ma_uint32 framesAvailablePlayback;
11473 for (;;) {
11474 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
11475 if (result != MA_SUCCESS) {
11476 break;
11477 }
11478
11479 if (framesAvailablePlayback >= pDevice->wasapi.actualPeriodSizeInFramesPlayback) {
11480 break;
11481 }
11482
11483 /*
11484 Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames
11485 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.
11486 */
11487 if (framesAvailablePlayback == prevFramesAvaialablePlayback) {
11488 break;
11489 }
11490 prevFramesAvaialablePlayback = framesAvailablePlayback;
11491
11492 WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE);
11493 ResetEvent(pDevice->wasapi.hEventPlayback); /* Manual reset. */
11494 }
11495 }
11496 }
11497
11498 hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
11499 if (FAILED(hr)) {
11500 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
11501 }
11502
11503 /* The audio client needs to be reset otherwise restarting will fail. */
11504 hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
11505 if (FAILED(hr)) {
11506 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
11507 }
11508
11509 ma_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
11510 }
11511
11512 return MA_SUCCESS;
11513}
11514
11515static ma_result ma_context_uninit__wasapi(ma_context* pContext)
11516{
11517 MA_ASSERT(pContext != NULL);
11518 MA_ASSERT(pContext->backend == ma_backend_wasapi);
11519 (void)pContext;
11520
11521 return MA_SUCCESS;
11522}
11523
11524static ma_result ma_context_init__wasapi(const ma_context_config* pConfig, ma_context* pContext)
11525{
11526 ma_result result = MA_SUCCESS;
11527
11528 MA_ASSERT(pContext != NULL);
11529
11530 (void)pConfig;
11531
11532#ifdef MA_WIN32_DESKTOP
11533 /*
11534 WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven
11535 exclusive mode does not work until SP1.
11536
11537 Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a lin error.
11538 */
11539 {
11540 ma_OSVERSIONINFOEXW osvi;
11541 ma_handle kernel32DLL;
11542 ma_PFNVerifyVersionInfoW _VerifyVersionInfoW;
11543 ma_PFNVerSetConditionMask _VerSetConditionMask;
11544
11545 kernel32DLL = ma_dlopen(pContext, "kernel32.dll");
11546 if (kernel32DLL == NULL) {
11547 return MA_NO_BACKEND;
11548 }
11549
11550 _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW)ma_dlsym(pContext, kernel32DLL, "VerifyVersionInfoW");
11551 _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(pContext, kernel32DLL, "VerSetConditionMask");
11552 if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) {
11553 ma_dlclose(pContext, kernel32DLL);
11554 return MA_NO_BACKEND;
11555 }
11556
11557 MA_ZERO_OBJECT(&osvi);
11558 osvi.dwOSVersionInfoSize = sizeof(osvi);
11559 osvi.dwMajorVersion = HIBYTE(MA_WIN32_WINNT_VISTA);
11560 osvi.dwMinorVersion = LOBYTE(MA_WIN32_WINNT_VISTA);
11561 osvi.wServicePackMajor = 1;
11562 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))) {
11563 result = MA_SUCCESS;
11564 } else {
11565 result = MA_NO_BACKEND;
11566 }
11567
11568 ma_dlclose(pContext, kernel32DLL);
11569 }
11570#endif
11571
11572 if (result != MA_SUCCESS) {
11573 return result;
11574 }
11575
11576 pContext->onUninit = ma_context_uninit__wasapi;
11577 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__wasapi;
11578 pContext->onEnumDevices = ma_context_enumerate_devices__wasapi;
11579 pContext->onGetDeviceInfo = ma_context_get_device_info__wasapi;
11580 pContext->onDeviceInit = ma_device_init__wasapi;
11581 pContext->onDeviceUninit = ma_device_uninit__wasapi;
11582 pContext->onDeviceStart = NULL; /* Not used. Started in onDeviceMainLoop. */
11583 pContext->onDeviceStop = ma_device_stop__wasapi; /* Required to ensure the capture event is signalled when stopping a loopback device while nothing is playing. */
11584 pContext->onDeviceMainLoop = ma_device_main_loop__wasapi;
11585
11586 return result;
11587}
11588#endif
11589
11590/******************************************************************************
11591
11592DirectSound Backend
11593
11594******************************************************************************/
11595#ifdef MA_HAS_DSOUND
11596/*#include <dsound.h>*/
11597
11598static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};
11599
11600/* miniaudio only uses priority or exclusive modes. */
11601#define MA_DSSCL_NORMAL 1
11602#define MA_DSSCL_PRIORITY 2
11603#define MA_DSSCL_EXCLUSIVE 3
11604#define MA_DSSCL_WRITEPRIMARY 4
11605
11606#define MA_DSCAPS_PRIMARYMONO 0x00000001
11607#define MA_DSCAPS_PRIMARYSTEREO 0x00000002
11608#define MA_DSCAPS_PRIMARY8BIT 0x00000004
11609#define MA_DSCAPS_PRIMARY16BIT 0x00000008
11610#define MA_DSCAPS_CONTINUOUSRATE 0x00000010
11611#define MA_DSCAPS_EMULDRIVER 0x00000020
11612#define MA_DSCAPS_CERTIFIED 0x00000040
11613#define MA_DSCAPS_SECONDARYMONO 0x00000100
11614#define MA_DSCAPS_SECONDARYSTEREO 0x00000200
11615#define MA_DSCAPS_SECONDARY8BIT 0x00000400
11616#define MA_DSCAPS_SECONDARY16BIT 0x00000800
11617
11618#define MA_DSBCAPS_PRIMARYBUFFER 0x00000001
11619#define MA_DSBCAPS_STATIC 0x00000002
11620#define MA_DSBCAPS_LOCHARDWARE 0x00000004
11621#define MA_DSBCAPS_LOCSOFTWARE 0x00000008
11622#define MA_DSBCAPS_CTRL3D 0x00000010
11623#define MA_DSBCAPS_CTRLFREQUENCY 0x00000020
11624#define MA_DSBCAPS_CTRLPAN 0x00000040
11625#define MA_DSBCAPS_CTRLVOLUME 0x00000080
11626#define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100
11627#define MA_DSBCAPS_CTRLFX 0x00000200
11628#define MA_DSBCAPS_STICKYFOCUS 0x00004000
11629#define MA_DSBCAPS_GLOBALFOCUS 0x00008000
11630#define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000
11631#define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000
11632#define MA_DSBCAPS_LOCDEFER 0x00040000
11633#define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000
11634
11635#define MA_DSBPLAY_LOOPING 0x00000001
11636#define MA_DSBPLAY_LOCHARDWARE 0x00000002
11637#define MA_DSBPLAY_LOCSOFTWARE 0x00000004
11638#define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008
11639#define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010
11640#define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020
11641
11642#define MA_DSCBSTART_LOOPING 0x00000001
11643
11644typedef struct
11645{
11646 DWORD dwSize;
11647 DWORD dwFlags;
11648 DWORD dwBufferBytes;
11649 DWORD dwReserved;
11650 WAVEFORMATEX* lpwfxFormat;
11651 GUID guid3DAlgorithm;
11652} MA_DSBUFFERDESC;
11653
11654typedef struct
11655{
11656 DWORD dwSize;
11657 DWORD dwFlags;
11658 DWORD dwBufferBytes;
11659 DWORD dwReserved;
11660 WAVEFORMATEX* lpwfxFormat;
11661 DWORD dwFXCount;
11662 void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */
11663} MA_DSCBUFFERDESC;
11664
11665typedef struct
11666{
11667 DWORD dwSize;
11668 DWORD dwFlags;
11669 DWORD dwMinSecondarySampleRate;
11670 DWORD dwMaxSecondarySampleRate;
11671 DWORD dwPrimaryBuffers;
11672 DWORD dwMaxHwMixingAllBuffers;
11673 DWORD dwMaxHwMixingStaticBuffers;
11674 DWORD dwMaxHwMixingStreamingBuffers;
11675 DWORD dwFreeHwMixingAllBuffers;
11676 DWORD dwFreeHwMixingStaticBuffers;
11677 DWORD dwFreeHwMixingStreamingBuffers;
11678 DWORD dwMaxHw3DAllBuffers;
11679 DWORD dwMaxHw3DStaticBuffers;
11680 DWORD dwMaxHw3DStreamingBuffers;
11681 DWORD dwFreeHw3DAllBuffers;
11682 DWORD dwFreeHw3DStaticBuffers;
11683 DWORD dwFreeHw3DStreamingBuffers;
11684 DWORD dwTotalHwMemBytes;
11685 DWORD dwFreeHwMemBytes;
11686 DWORD dwMaxContigFreeHwMemBytes;
11687 DWORD dwUnlockTransferRateHwBuffers;
11688 DWORD dwPlayCpuOverheadSwBuffers;
11689 DWORD dwReserved1;
11690 DWORD dwReserved2;
11691} MA_DSCAPS;
11692
11693typedef struct
11694{
11695 DWORD dwSize;
11696 DWORD dwFlags;
11697 DWORD dwBufferBytes;
11698 DWORD dwUnlockTransferRate;
11699 DWORD dwPlayCpuOverhead;
11700} MA_DSBCAPS;
11701
11702typedef struct
11703{
11704 DWORD dwSize;
11705 DWORD dwFlags;
11706 DWORD dwFormats;
11707 DWORD dwChannels;
11708} MA_DSCCAPS;
11709
11710typedef struct
11711{
11712 DWORD dwSize;
11713 DWORD dwFlags;
11714 DWORD dwBufferBytes;
11715 DWORD dwReserved;
11716} MA_DSCBCAPS;
11717
11718typedef struct
11719{
11720 DWORD dwOffset;
11721 HANDLE hEventNotify;
11722} MA_DSBPOSITIONNOTIFY;
11723
11724typedef struct ma_IDirectSound ma_IDirectSound;
11725typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer;
11726typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture;
11727typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer;
11728typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify;
11729
11730
11731/*
11732COM objects. The way these work is that you have a vtable (a list of function pointers, kind of
11733like how C++ works internally), and then you have a structure with a single member, which is a
11734pointer to the vtable. The vtable is where the methods of the object are defined. Methods need
11735to be in a specific order, and parent classes need to have their methods declared first.
11736*/
11737
11738/* IDirectSound */
11739typedef struct
11740{
11741 /* IUnknown */
11742 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject);
11743 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis);
11744 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis);
11745
11746 /* IDirectSound */
11747 HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter);
11748 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps);
11749 HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate);
11750 HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel);
11751 HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis);
11752 HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig);
11753 HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig);
11754 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice);
11755} ma_IDirectSoundVtbl;
11756struct ma_IDirectSound
11757{
11758 ma_IDirectSoundVtbl* lpVtbl;
11759};
11760static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
11761static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); }
11762static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); }
11763static 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); }
11764static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); }
11765static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); }
11766static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); }
11767static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); }
11768static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); }
11769static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); }
11770static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
11771
11772
11773/* IDirectSoundBuffer */
11774typedef struct
11775{
11776 /* IUnknown */
11777 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject);
11778 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis);
11779 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis);
11780
11781 /* IDirectSoundBuffer */
11782 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps);
11783 HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor);
11784 HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
11785 HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume);
11786 HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan);
11787 HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency);
11788 HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus);
11789 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc);
11790 HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
11791 HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags);
11792 HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition);
11793 HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat);
11794 HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume);
11795 HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan);
11796 HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency);
11797 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis);
11798 HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
11799 HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis);
11800} ma_IDirectSoundBufferVtbl;
11801struct ma_IDirectSoundBuffer
11802{
11803 ma_IDirectSoundBufferVtbl* lpVtbl;
11804};
11805static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
11806static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
11807static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
11808static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); }
11809static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); }
11810static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
11811static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); }
11812static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); }
11813static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); }
11814static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
11815static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); }
11816static 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); }
11817static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); }
11818static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); }
11819static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); }
11820static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); }
11821static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); }
11822static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); }
11823static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
11824static 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); }
11825static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); }
11826
11827
11828/* IDirectSoundCapture */
11829typedef struct
11830{
11831 /* IUnknown */
11832 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject);
11833 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis);
11834 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis);
11835
11836 /* IDirectSoundCapture */
11837 HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter);
11838 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps);
11839 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice);
11840} ma_IDirectSoundCaptureVtbl;
11841struct ma_IDirectSoundCapture
11842{
11843 ma_IDirectSoundCaptureVtbl* lpVtbl;
11844};
11845static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
11846static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef(ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); }
11847static MA_INLINE ULONG ma_IDirectSoundCapture_Release(ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); }
11848static 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); }
11849static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); }
11850static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
11851
11852
11853/* IDirectSoundCaptureBuffer */
11854typedef struct
11855{
11856 /* IUnknown */
11857 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject);
11858 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis);
11859 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis);
11860
11861 /* IDirectSoundCaptureBuffer */
11862 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps);
11863 HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition);
11864 HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
11865 HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus);
11866 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc);
11867 HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
11868 HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags);
11869 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis);
11870 HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
11871} ma_IDirectSoundCaptureBufferVtbl;
11872struct ma_IDirectSoundCaptureBuffer
11873{
11874 ma_IDirectSoundCaptureBufferVtbl* lpVtbl;
11875};
11876static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
11877static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
11878static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
11879static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); }
11880static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); }
11881static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
11882static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
11883static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); }
11884static 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); }
11885static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); }
11886static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
11887static 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); }
11888
11889
11890/* IDirectSoundNotify */
11891typedef struct
11892{
11893 /* IUnknown */
11894 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject);
11895 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis);
11896 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis);
11897
11898 /* IDirectSoundNotify */
11899 HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies);
11900} ma_IDirectSoundNotifyVtbl;
11901struct ma_IDirectSoundNotify
11902{
11903 ma_IDirectSoundNotifyVtbl* lpVtbl;
11904};
11905static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
11906static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); }
11907static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); }
11908static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); }
11909
11910
11911typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (LPGUID pDeviceGUID, LPCSTR pDeviceDescription, LPCSTR pModule, LPVOID pContext);
11912typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, LPUNKNOWN pUnkOuter);
11913typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext);
11914typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, LPUNKNOWN pUnkOuter);
11915typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext);
11916
11917static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax)
11918{
11919 /* Normalize the range in case we were given something stupid. */
11920 if (sampleRateMin < MA_MIN_SAMPLE_RATE) {
11921 sampleRateMin = MA_MIN_SAMPLE_RATE;
11922 }
11923 if (sampleRateMax > MA_MAX_SAMPLE_RATE) {
11924 sampleRateMax = MA_MAX_SAMPLE_RATE;
11925 }
11926 if (sampleRateMin > sampleRateMax) {
11927 sampleRateMin = sampleRateMax;
11928 }
11929
11930 if (sampleRateMin == sampleRateMax) {
11931 return sampleRateMax;
11932 } else {
11933 size_t iStandardRate;
11934 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
11935 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
11936 if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) {
11937 return standardRate;
11938 }
11939 }
11940 }
11941
11942 /* Should never get here. */
11943 MA_ASSERT(MA_FALSE);
11944 return 0;
11945}
11946
11947/*
11948Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown,
11949the channel count and channel map will be left unmodified.
11950*/
11951static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut)
11952{
11953 WORD channels;
11954 DWORD channelMap;
11955
11956 channels = 0;
11957 if (pChannelsOut != NULL) {
11958 channels = *pChannelsOut;
11959 }
11960
11961 channelMap = 0;
11962 if (pChannelMapOut != NULL) {
11963 channelMap = *pChannelMapOut;
11964 }
11965
11966 /*
11967 The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper
11968 16 bits is for the geometry.
11969 */
11970 switch ((BYTE)(speakerConfig)) {
11971 case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
11972 case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break;
11973 case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
11974 case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
11975 case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break;
11976 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;
11977 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;
11978 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;
11979 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;
11980 default: break;
11981 }
11982
11983 if (pChannelsOut != NULL) {
11984 *pChannelsOut = channels;
11985 }
11986
11987 if (pChannelMapOut != NULL) {
11988 *pChannelMapOut = channelMap;
11989 }
11990}
11991
11992
11993static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound)
11994{
11995 ma_IDirectSound* pDirectSound;
11996 HWND hWnd;
11997
11998 MA_ASSERT(pContext != NULL);
11999 MA_ASSERT(ppDirectSound != NULL);
12000
12001 *ppDirectSound = NULL;
12002 pDirectSound = NULL;
12003
12004 if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) {
12005 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
12006 }
12007
12008 /* The cooperative level must be set before doing anything else. */
12009 hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();
12010 if (hWnd == NULL) {
12011 hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();
12012 }
12013 if (FAILED(ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY))) {
12014 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.", MA_SHARE_MODE_NOT_SUPPORTED);
12015 }
12016
12017 *ppDirectSound = pDirectSound;
12018 return MA_SUCCESS;
12019}
12020
12021static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture)
12022{
12023 ma_IDirectSoundCapture* pDirectSoundCapture;
12024
12025 MA_ASSERT(pContext != NULL);
12026 MA_ASSERT(ppDirectSoundCapture != NULL);
12027
12028 /* DirectSound does not support exclusive mode for capture. */
12029 if (shareMode == ma_share_mode_exclusive) {
12030 return MA_SHARE_MODE_NOT_SUPPORTED;
12031 }
12032
12033 *ppDirectSoundCapture = NULL;
12034 pDirectSoundCapture = NULL;
12035
12036 if (FAILED(((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL))) {
12037 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
12038 }
12039
12040 *ppDirectSoundCapture = pDirectSoundCapture;
12041 return MA_SUCCESS;
12042}
12043
12044static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate)
12045{
12046 MA_DSCCAPS caps;
12047 WORD bitsPerSample;
12048 DWORD sampleRate;
12049
12050 MA_ASSERT(pContext != NULL);
12051 MA_ASSERT(pDirectSoundCapture != NULL);
12052
12053 if (pChannels) {
12054 *pChannels = 0;
12055 }
12056 if (pBitsPerSample) {
12057 *pBitsPerSample = 0;
12058 }
12059 if (pSampleRate) {
12060 *pSampleRate = 0;
12061 }
12062
12063 MA_ZERO_OBJECT(&caps);
12064 caps.dwSize = sizeof(caps);
12065 if (FAILED(ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps))) {
12066 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
12067 }
12068
12069 if (pChannels) {
12070 *pChannels = (WORD)caps.dwChannels;
12071 }
12072
12073 /* 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. */
12074 bitsPerSample = 16;
12075 sampleRate = 48000;
12076
12077 if (caps.dwChannels == 1) {
12078 if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) {
12079 sampleRate = 48000;
12080 } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) {
12081 sampleRate = 44100;
12082 } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) {
12083 sampleRate = 22050;
12084 } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) {
12085 sampleRate = 11025;
12086 } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) {
12087 sampleRate = 96000;
12088 } else {
12089 bitsPerSample = 8;
12090 if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) {
12091 sampleRate = 48000;
12092 } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) {
12093 sampleRate = 44100;
12094 } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) {
12095 sampleRate = 22050;
12096 } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) {
12097 sampleRate = 11025;
12098 } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) {
12099 sampleRate = 96000;
12100 } else {
12101 bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
12102 }
12103 }
12104 } else if (caps.dwChannels == 2) {
12105 if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) {
12106 sampleRate = 48000;
12107 } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) {
12108 sampleRate = 44100;
12109 } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) {
12110 sampleRate = 22050;
12111 } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) {
12112 sampleRate = 11025;
12113 } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) {
12114 sampleRate = 96000;
12115 } else {
12116 bitsPerSample = 8;
12117 if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) {
12118 sampleRate = 48000;
12119 } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) {
12120 sampleRate = 44100;
12121 } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) {
12122 sampleRate = 22050;
12123 } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) {
12124 sampleRate = 11025;
12125 } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) {
12126 sampleRate = 96000;
12127 } else {
12128 bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
12129 }
12130 }
12131 }
12132
12133 if (pBitsPerSample) {
12134 *pBitsPerSample = bitsPerSample;
12135 }
12136 if (pSampleRate) {
12137 *pSampleRate = sampleRate;
12138 }
12139
12140 return MA_SUCCESS;
12141}
12142
12143static ma_bool32 ma_context_is_device_id_equal__dsound(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
12144{
12145 MA_ASSERT(pContext != NULL);
12146 MA_ASSERT(pID0 != NULL);
12147 MA_ASSERT(pID1 != NULL);
12148 (void)pContext;
12149
12150 return memcmp(pID0->dsound, pID1->dsound, sizeof(pID0->dsound)) == 0;
12151}
12152
12153
12154typedef struct
12155{
12156 ma_context* pContext;
12157 ma_device_type deviceType;
12158 ma_enum_devices_callback_proc callback;
12159 void* pUserData;
12160 ma_bool32 terminated;
12161} ma_context_enumerate_devices_callback_data__dsound;
12162
12163static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
12164{
12165 ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext;
12166 ma_device_info deviceInfo;
12167
12168 MA_ZERO_OBJECT(&deviceInfo);
12169
12170 /* ID. */
12171 if (lpGuid != NULL) {
12172 MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16);
12173 } else {
12174 MA_ZERO_MEMORY(deviceInfo.id.dsound, 16);
12175 }
12176
12177 /* Name / Description */
12178 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1);
12179
12180
12181 /* Call the callback function, but make sure we stop enumerating if the callee requested so. */
12182 MA_ASSERT(pData != NULL);
12183 pData->terminated = !pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData);
12184 if (pData->terminated) {
12185 return FALSE; /* Stop enumeration. */
12186 } else {
12187 return TRUE; /* Continue enumeration. */
12188 }
12189
12190 (void)lpcstrModule;
12191}
12192
12193static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
12194{
12195 ma_context_enumerate_devices_callback_data__dsound data;
12196
12197 MA_ASSERT(pContext != NULL);
12198 MA_ASSERT(callback != NULL);
12199
12200 data.pContext = pContext;
12201 data.callback = callback;
12202 data.pUserData = pUserData;
12203 data.terminated = MA_FALSE;
12204
12205 /* Playback. */
12206 if (!data.terminated) {
12207 data.deviceType = ma_device_type_playback;
12208 ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
12209 }
12210
12211 /* Capture. */
12212 if (!data.terminated) {
12213 data.deviceType = ma_device_type_capture;
12214 ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
12215 }
12216
12217 return MA_SUCCESS;
12218}
12219
12220
12221typedef struct
12222{
12223 const ma_device_id* pDeviceID;
12224 ma_device_info* pDeviceInfo;
12225 ma_bool32 found;
12226} ma_context_get_device_info_callback_data__dsound;
12227
12228static BOOL CALLBACK ma_context_get_device_info_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
12229{
12230 ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext;
12231 MA_ASSERT(pData != NULL);
12232
12233 if ((pData->pDeviceID == NULL || ma_is_guid_equal(pData->pDeviceID->dsound, &MA_GUID_NULL)) && (lpGuid == NULL || ma_is_guid_equal(lpGuid, &MA_GUID_NULL))) {
12234 /* Default device. */
12235 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
12236 pData->found = MA_TRUE;
12237 return FALSE; /* Stop enumeration. */
12238 } else {
12239 /* Not the default device. */
12240 if (lpGuid != NULL && pData->pDeviceID != NULL) {
12241 if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) {
12242 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
12243 pData->found = MA_TRUE;
12244 return FALSE; /* Stop enumeration. */
12245 }
12246 }
12247 }
12248
12249 (void)lpcstrModule;
12250 return TRUE;
12251}
12252
12253static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
12254{
12255 /* Exclusive mode and capture not supported with DirectSound. */
12256 if (deviceType == ma_device_type_capture && shareMode == ma_share_mode_exclusive) {
12257 return MA_SHARE_MODE_NOT_SUPPORTED;
12258 }
12259
12260 if (pDeviceID != NULL) {
12261 ma_context_get_device_info_callback_data__dsound data;
12262
12263 /* ID. */
12264 MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16);
12265
12266 /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */
12267 data.pDeviceID = pDeviceID;
12268 data.pDeviceInfo = pDeviceInfo;
12269 data.found = MA_FALSE;
12270 if (deviceType == ma_device_type_playback) {
12271 ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
12272 } else {
12273 ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
12274 }
12275
12276 if (!data.found) {
12277 return MA_NO_DEVICE;
12278 }
12279 } else {
12280 /* 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. */
12281
12282 /* ID */
12283 MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16);
12284
12285 /* Name / Description */
12286 if (deviceType == ma_device_type_playback) {
12287 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
12288 } else {
12289 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
12290 }
12291 }
12292
12293 /* Retrieving detailed information is slightly different depending on the device type. */
12294 if (deviceType == ma_device_type_playback) {
12295 /* Playback. */
12296 ma_IDirectSound* pDirectSound;
12297 ma_result result;
12298 MA_DSCAPS caps;
12299 ma_uint32 iFormat;
12300
12301 result = ma_context_create_IDirectSound__dsound(pContext, shareMode, pDeviceID, &pDirectSound);
12302 if (result != MA_SUCCESS) {
12303 return result;
12304 }
12305
12306 MA_ZERO_OBJECT(&caps);
12307 caps.dwSize = sizeof(caps);
12308 if (FAILED(ma_IDirectSound_GetCaps(pDirectSound, &caps))) {
12309 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
12310 }
12311
12312 if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
12313 /* It supports at least stereo, but could support more. */
12314 WORD channels = 2;
12315
12316 /* Look at the speaker configuration to get a better idea on the channel count. */
12317 DWORD speakerConfig;
12318 if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig))) {
12319 ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL);
12320 }
12321
12322 pDeviceInfo->minChannels = channels;
12323 pDeviceInfo->maxChannels = channels;
12324 } else {
12325 /* It does not support stereo, which means we are stuck with mono. */
12326 pDeviceInfo->minChannels = 1;
12327 pDeviceInfo->maxChannels = 1;
12328 }
12329
12330 /* Sample rate. */
12331 if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
12332 pDeviceInfo->minSampleRate = caps.dwMinSecondarySampleRate;
12333 pDeviceInfo->maxSampleRate = caps.dwMaxSecondarySampleRate;
12334
12335 /*
12336 On my machine the min and max sample rates can return 100 and 200000 respectively. I'd rather these be within
12337 the range of our standard sample rates so I'm clamping.
12338 */
12339 if (caps.dwMinSecondarySampleRate < MA_MIN_SAMPLE_RATE && caps.dwMaxSecondarySampleRate >= MA_MIN_SAMPLE_RATE) {
12340 pDeviceInfo->minSampleRate = MA_MIN_SAMPLE_RATE;
12341 }
12342 if (caps.dwMaxSecondarySampleRate > MA_MAX_SAMPLE_RATE && caps.dwMinSecondarySampleRate <= MA_MAX_SAMPLE_RATE) {
12343 pDeviceInfo->maxSampleRate = MA_MAX_SAMPLE_RATE;
12344 }
12345 } else {
12346 /* Only supports a single sample rate. Set both min an max to the same thing. Do not clamp within the standard rates. */
12347 pDeviceInfo->minSampleRate = caps.dwMaxSecondarySampleRate;
12348 pDeviceInfo->maxSampleRate = caps.dwMaxSecondarySampleRate;
12349 }
12350
12351 /* DirectSound can support all formats. */
12352 pDeviceInfo->formatCount = ma_format_count - 1; /* Minus one because we don't want to include ma_format_unknown. */
12353 for (iFormat = 0; iFormat < pDeviceInfo->formatCount; ++iFormat) {
12354 pDeviceInfo->formats[iFormat] = (ma_format)(iFormat + 1); /* +1 to skip over ma_format_unknown. */
12355 }
12356
12357 ma_IDirectSound_Release(pDirectSound);
12358 } else {
12359 /*
12360 Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture
12361 devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just
12362 reporting the best format.
12363 */
12364 ma_IDirectSoundCapture* pDirectSoundCapture;
12365 ma_result result;
12366 WORD channels;
12367 WORD bitsPerSample;
12368 DWORD sampleRate;
12369
12370 result = ma_context_create_IDirectSoundCapture__dsound(pContext, shareMode, pDeviceID, &pDirectSoundCapture);
12371 if (result != MA_SUCCESS) {
12372 return result;
12373 }
12374
12375 result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate);
12376 if (result != MA_SUCCESS) {
12377 ma_IDirectSoundCapture_Release(pDirectSoundCapture);
12378 return result;
12379 }
12380
12381 pDeviceInfo->minChannels = channels;
12382 pDeviceInfo->maxChannels = channels;
12383 pDeviceInfo->minSampleRate = sampleRate;
12384 pDeviceInfo->maxSampleRate = sampleRate;
12385 pDeviceInfo->formatCount = 1;
12386 if (bitsPerSample == 8) {
12387 pDeviceInfo->formats[0] = ma_format_u8;
12388 } else if (bitsPerSample == 16) {
12389 pDeviceInfo->formats[0] = ma_format_s16;
12390 } else if (bitsPerSample == 24) {
12391 pDeviceInfo->formats[0] = ma_format_s24;
12392 } else if (bitsPerSample == 32) {
12393 pDeviceInfo->formats[0] = ma_format_s32;
12394 } else {
12395 ma_IDirectSoundCapture_Release(pDirectSoundCapture);
12396 return MA_FORMAT_NOT_SUPPORTED;
12397 }
12398
12399 ma_IDirectSoundCapture_Release(pDirectSoundCapture);
12400 }
12401
12402 return MA_SUCCESS;
12403}
12404
12405
12406
12407static void ma_device_uninit__dsound(ma_device* pDevice)
12408{
12409 MA_ASSERT(pDevice != NULL);
12410
12411 if (pDevice->dsound.pCaptureBuffer != NULL) {
12412 ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
12413 }
12414 if (pDevice->dsound.pCapture != NULL) {
12415 ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture);
12416 }
12417
12418 if (pDevice->dsound.pPlaybackBuffer != NULL) {
12419 ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
12420 }
12421 if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) {
12422 ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer);
12423 }
12424 if (pDevice->dsound.pPlayback != NULL) {
12425 ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback);
12426 }
12427}
12428
12429static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, WAVEFORMATEXTENSIBLE* pWF)
12430{
12431 GUID subformat;
12432
12433 switch (format)
12434 {
12435 case ma_format_u8:
12436 case ma_format_s16:
12437 case ma_format_s24:
12438 /*case ma_format_s24_32:*/
12439 case ma_format_s32:
12440 {
12441 subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
12442 } break;
12443
12444 case ma_format_f32:
12445 {
12446 subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
12447 } break;
12448
12449 default:
12450 return MA_FORMAT_NOT_SUPPORTED;
12451 }
12452
12453 MA_ZERO_OBJECT(pWF);
12454 pWF->Format.cbSize = sizeof(*pWF);
12455 pWF->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
12456 pWF->Format.nChannels = (WORD)channels;
12457 pWF->Format.nSamplesPerSec = (DWORD)sampleRate;
12458 pWF->Format.wBitsPerSample = (WORD)ma_get_bytes_per_sample(format)*8;
12459 pWF->Format.nBlockAlign = (pWF->Format.nChannels * pWF->Format.wBitsPerSample) / 8;
12460 pWF->Format.nAvgBytesPerSec = pWF->Format.nBlockAlign * pWF->Format.nSamplesPerSec;
12461 pWF->Samples.wValidBitsPerSample = pWF->Format.wBitsPerSample;
12462 pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels);
12463 pWF->SubFormat = subformat;
12464
12465 return MA_SUCCESS;
12466}
12467
12468static ma_result ma_device_init__dsound(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
12469{
12470 ma_result result;
12471 ma_uint32 periodSizeInMilliseconds;
12472
12473 MA_ASSERT(pDevice != NULL);
12474 MA_ZERO_OBJECT(&pDevice->dsound);
12475
12476 if (pConfig->deviceType == ma_device_type_loopback) {
12477 return MA_DEVICE_TYPE_NOT_SUPPORTED;
12478 }
12479
12480 periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
12481 if (periodSizeInMilliseconds == 0) {
12482 periodSizeInMilliseconds = ma_calculate_buffer_size_in_milliseconds_from_frames(pConfig->periodSizeInFrames, pConfig->sampleRate);
12483 }
12484
12485 /* DirectSound should use a latency of about 20ms per period for low latency mode. */
12486 if (pDevice->usingDefaultBufferSize) {
12487 if (pConfig->performanceProfile == ma_performance_profile_low_latency) {
12488 periodSizeInMilliseconds = 20;
12489 } else {
12490 periodSizeInMilliseconds = 200;
12491 }
12492 }
12493
12494 /* DirectSound breaks down with tiny buffer sizes (bad glitching and silent output). I am therefore restricting the size of the buffer to a minimum of 20 milliseconds. */
12495 if (periodSizeInMilliseconds < 20) {
12496 periodSizeInMilliseconds = 20;
12497 }
12498
12499 /*
12500 Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize
12501 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
12502 full-duplex mode.
12503 */
12504 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
12505 WAVEFORMATEXTENSIBLE wf;
12506 MA_DSCBUFFERDESC descDS;
12507 ma_uint32 periodSizeInFrames;
12508 char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
12509 WAVEFORMATEXTENSIBLE* pActualFormat;
12510
12511 result = ma_config_to_WAVEFORMATEXTENSIBLE(pConfig->capture.format, pConfig->capture.channels, pConfig->sampleRate, pConfig->capture.channelMap, &wf);
12512 if (result != MA_SUCCESS) {
12513 return result;
12514 }
12515
12516 result = ma_context_create_IDirectSoundCapture__dsound(pContext, pConfig->capture.shareMode, pConfig->capture.pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture);
12517 if (result != MA_SUCCESS) {
12518 ma_device_uninit__dsound(pDevice);
12519 return result;
12520 }
12521
12522 result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.Format.nChannels, &wf.Format.wBitsPerSample, &wf.Format.nSamplesPerSec);
12523 if (result != MA_SUCCESS) {
12524 ma_device_uninit__dsound(pDevice);
12525 return result;
12526 }
12527
12528 wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
12529 wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
12530 wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample;
12531 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
12532
12533 /* The size of the buffer must be a clean multiple of the period count. */
12534 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, wf.Format.nSamplesPerSec);
12535
12536 MA_ZERO_OBJECT(&descDS);
12537 descDS.dwSize = sizeof(descDS);
12538 descDS.dwFlags = 0;
12539 descDS.dwBufferBytes = periodSizeInFrames * pConfig->periods * ma_get_bytes_per_frame(pDevice->capture.internalFormat, wf.Format.nChannels);
12540 descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
12541 if (FAILED(ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL))) {
12542 ma_device_uninit__dsound(pDevice);
12543 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
12544 }
12545
12546 /* Get the _actual_ properties of the buffer. */
12547 pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata;
12548 if (FAILED(ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL))) {
12549 ma_device_uninit__dsound(pDevice);
12550 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer.", MA_FORMAT_NOT_SUPPORTED);
12551 }
12552
12553 pDevice->capture.internalFormat = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat);
12554 pDevice->capture.internalChannels = pActualFormat->Format.nChannels;
12555 pDevice->capture.internalSampleRate = pActualFormat->Format.nSamplesPerSec;
12556
12557 /* Get the internal channel map based on the channel mask. */
12558 if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
12559 ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
12560 } else {
12561 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
12562 }
12563
12564 /*
12565 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
12566 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.
12567 */
12568 if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels) / pConfig->periods)) {
12569 descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, wf.Format.nChannels) * pConfig->periods;
12570 ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
12571
12572 if (FAILED(ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL))) {
12573 ma_device_uninit__dsound(pDevice);
12574 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
12575 }
12576 }
12577
12578 /* DirectSound should give us a buffer exactly the size we asked for. */
12579 pDevice->capture.internalPeriodSizeInFrames = periodSizeInFrames;
12580 pDevice->capture.internalPeriods = pConfig->periods;
12581 }
12582
12583 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
12584 WAVEFORMATEXTENSIBLE wf;
12585 MA_DSBUFFERDESC descDSPrimary;
12586 MA_DSCAPS caps;
12587 char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
12588 WAVEFORMATEXTENSIBLE* pActualFormat;
12589 ma_uint32 periodSizeInFrames;
12590 MA_DSBUFFERDESC descDS;
12591
12592 result = ma_config_to_WAVEFORMATEXTENSIBLE(pConfig->playback.format, pConfig->playback.channels, pConfig->sampleRate, pConfig->playback.channelMap, &wf);
12593 if (result != MA_SUCCESS) {
12594 return result;
12595 }
12596
12597 result = ma_context_create_IDirectSound__dsound(pContext, pConfig->playback.shareMode, pConfig->playback.pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback);
12598 if (result != MA_SUCCESS) {
12599 ma_device_uninit__dsound(pDevice);
12600 return result;
12601 }
12602
12603 MA_ZERO_OBJECT(&descDSPrimary);
12604 descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC);
12605 descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME;
12606 if (FAILED(ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL))) {
12607 ma_device_uninit__dsound(pDevice);
12608 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
12609 }
12610
12611
12612 /* We may want to make some adjustments to the format if we are using defaults. */
12613 MA_ZERO_OBJECT(&caps);
12614 caps.dwSize = sizeof(caps);
12615 if (FAILED(ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps))) {
12616 ma_device_uninit__dsound(pDevice);
12617 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
12618 }
12619
12620 if (pDevice->playback.usingDefaultChannels) {
12621 if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
12622 DWORD speakerConfig;
12623
12624 /* It supports at least stereo, but could support more. */
12625 wf.Format.nChannels = 2;
12626
12627 /* Look at the speaker configuration to get a better idea on the channel count. */
12628 if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) {
12629 ma_get_channels_from_speaker_config__dsound(speakerConfig, &wf.Format.nChannels, &wf.dwChannelMask);
12630 }
12631 } else {
12632 /* It does not support stereo, which means we are stuck with mono. */
12633 wf.Format.nChannels = 1;
12634 }
12635 }
12636
12637 if (pDevice->usingDefaultSampleRate) {
12638 /* We base the sample rate on the values returned by GetCaps(). */
12639 if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
12640 wf.Format.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate);
12641 } else {
12642 wf.Format.nSamplesPerSec = caps.dwMaxSecondarySampleRate;
12643 }
12644 }
12645
12646 wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
12647 wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
12648
12649 /*
12650 From MSDN:
12651
12652 The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest
12653 supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer
12654 and compare the result with the format that was requested with the SetFormat method.
12655 */
12656 if (FAILED(ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)&wf))) {
12657 ma_device_uninit__dsound(pDevice);
12658 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer.", MA_FORMAT_NOT_SUPPORTED);
12659 }
12660
12661 /* Get the _actual_ properties of the buffer. */
12662 pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata;
12663 if (FAILED(ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL))) {
12664 ma_device_uninit__dsound(pDevice);
12665 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer.", MA_FORMAT_NOT_SUPPORTED);
12666 }
12667
12668 pDevice->playback.internalFormat = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat);
12669 pDevice->playback.internalChannels = pActualFormat->Format.nChannels;
12670 pDevice->playback.internalSampleRate = pActualFormat->Format.nSamplesPerSec;
12671
12672 /* Get the internal channel map based on the channel mask. */
12673 if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
12674 ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
12675 } else {
12676 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
12677 }
12678
12679 /* The size of the buffer must be a clean multiple of the period count. */
12680 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, pDevice->playback.internalSampleRate);
12681
12682 /*
12683 Meaning of dwFlags (from MSDN):
12684
12685 DSBCAPS_CTRLPOSITIONNOTIFY
12686 The buffer has position notification capability.
12687
12688 DSBCAPS_GLOBALFOCUS
12689 With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to
12690 another application, even if the new application uses DirectSound.
12691
12692 DSBCAPS_GETCURRENTPOSITION2
12693 In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated
12694 sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the
12695 application can get a more accurate play cursor.
12696 */
12697 MA_ZERO_OBJECT(&descDS);
12698 descDS.dwSize = sizeof(descDS);
12699 descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2;
12700 descDS.dwBufferBytes = periodSizeInFrames * pConfig->periods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
12701 descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
12702 if (FAILED(ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL))) {
12703 ma_device_uninit__dsound(pDevice);
12704 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
12705 }
12706
12707 /* DirectSound should give us a buffer exactly the size we asked for. */
12708 pDevice->playback.internalPeriodSizeInFrames = periodSizeInFrames;
12709 pDevice->playback.internalPeriods = pConfig->periods;
12710 }
12711
12712 (void)pContext;
12713 return MA_SUCCESS;
12714}
12715
12716
12717static ma_result ma_device_main_loop__dsound(ma_device* pDevice)
12718{
12719 ma_result result = MA_SUCCESS;
12720 ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
12721 ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
12722 HRESULT hr;
12723 DWORD lockOffsetInBytesCapture;
12724 DWORD lockSizeInBytesCapture;
12725 DWORD mappedSizeInBytesCapture;
12726 DWORD mappedDeviceFramesProcessedCapture;
12727 void* pMappedDeviceBufferCapture;
12728 DWORD lockOffsetInBytesPlayback;
12729 DWORD lockSizeInBytesPlayback;
12730 DWORD mappedSizeInBytesPlayback;
12731 void* pMappedDeviceBufferPlayback;
12732 DWORD prevReadCursorInBytesCapture = 0;
12733 DWORD prevPlayCursorInBytesPlayback = 0;
12734 ma_bool32 physicalPlayCursorLoopFlagPlayback = 0;
12735 DWORD virtualWriteCursorInBytesPlayback = 0;
12736 ma_bool32 virtualWriteCursorLoopFlagPlayback = 0;
12737 ma_bool32 isPlaybackDeviceStarted = MA_FALSE;
12738 ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */
12739 ma_uint32 waitTimeInMilliseconds = 1;
12740
12741 MA_ASSERT(pDevice != NULL);
12742
12743 /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */
12744 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
12745 if (FAILED(ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING))) {
12746 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
12747 }
12748 }
12749
12750 while (ma_device__get_state(pDevice) == MA_STATE_STARTED) {
12751 switch (pDevice->type)
12752 {
12753 case ma_device_type_duplex:
12754 {
12755 DWORD physicalCaptureCursorInBytes;
12756 DWORD physicalReadCursorInBytes;
12757 if (FAILED(ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes))) {
12758 return MA_ERROR;
12759 }
12760
12761 /* If nothing is available we just sleep for a bit and return from this iteration. */
12762 if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) {
12763 ma_sleep(waitTimeInMilliseconds);
12764 continue; /* Nothing is available in the capture buffer. */
12765 }
12766
12767 /*
12768 The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure
12769 we don't return until every frame has been copied over.
12770 */
12771 if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
12772 /* The capture position has not looped. This is the simple case. */
12773 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
12774 lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
12775 } else {
12776 /*
12777 The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
12778 do it again from the start.
12779 */
12780 if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
12781 /* Lock up to the end of the buffer. */
12782 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
12783 lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
12784 } else {
12785 /* Lock starting from the start of the buffer. */
12786 lockOffsetInBytesCapture = 0;
12787 lockSizeInBytesCapture = physicalReadCursorInBytes;
12788 }
12789 }
12790
12791 if (lockSizeInBytesCapture == 0) {
12792 ma_sleep(waitTimeInMilliseconds);
12793 continue; /* Nothing is available in the capture buffer. */
12794 }
12795
12796 hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
12797 if (FAILED(hr)) {
12798 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_FAILED_TO_MAP_DEVICE_BUFFER);
12799 }
12800
12801
12802 /* 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. */
12803 mappedDeviceFramesProcessedCapture = 0;
12804
12805 for (;;) { /* Keep writing to the playback device. */
12806 ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
12807 ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
12808 ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
12809 ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
12810 ma_uint32 outputFramesInClientFormatCount;
12811 ma_uint32 outputFramesInClientFormatConsumed = 0;
12812 ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap);
12813 ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture;
12814 void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture);
12815
12816 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess);
12817 if (result != MA_SUCCESS) {
12818 break;
12819 }
12820
12821 outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess;
12822 mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess;
12823
12824 ma_device__on_data(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess);
12825
12826 /* 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. */
12827 for (;;) {
12828 ma_uint32 framesWrittenThisIteration;
12829 DWORD physicalPlayCursorInBytes;
12830 DWORD physicalWriteCursorInBytes;
12831 DWORD availableBytesPlayback;
12832 DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */
12833
12834 /* We need the physical play and write cursors. */
12835 if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
12836 break;
12837 }
12838
12839 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
12840 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
12841 }
12842 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
12843
12844 /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
12845 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
12846 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
12847 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
12848 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
12849 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
12850 } else {
12851 /* This is an error. */
12852 #ifdef MA_DEBUG_OUTPUT
12853 printf("[DirectSound] (Duplex/Playback) WARNING: Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%d, virtualWriteCursorInBytes=%d.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
12854 #endif
12855 availableBytesPlayback = 0;
12856 }
12857 } else {
12858 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
12859 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
12860 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
12861 } else {
12862 /* This is an error. */
12863 #ifdef MA_DEBUG_OUTPUT
12864 printf("[DirectSound] (Duplex/Playback) WARNING: Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%d, virtualWriteCursorInBytes=%d.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
12865 #endif
12866 availableBytesPlayback = 0;
12867 }
12868 }
12869
12870 #ifdef MA_DEBUG_OUTPUT
12871 /*printf("[DirectSound] (Duplex/Playback) physicalPlayCursorInBytes=%d, availableBytesPlayback=%d\n", physicalPlayCursorInBytes, availableBytesPlayback);*/
12872 #endif
12873
12874 /* If there's no room available for writing we need to wait for more. */
12875 if (availableBytesPlayback == 0) {
12876 /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
12877 if (!isPlaybackDeviceStarted) {
12878 if (FAILED(ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING))) {
12879 ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
12880 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
12881 }
12882 isPlaybackDeviceStarted = MA_TRUE;
12883 } else {
12884 ma_sleep(waitTimeInMilliseconds);
12885 continue;
12886 }
12887 }
12888
12889
12890 /* 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. */
12891 lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
12892 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
12893 /* Same loop iteration. Go up to the end of the buffer. */
12894 lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
12895 } else {
12896 /* Different loop iterations. Go up to the physical play cursor. */
12897 lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
12898 }
12899
12900 hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
12901 if (FAILED(hr)) {
12902 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_FAILED_TO_MAP_DEVICE_BUFFER);
12903 break;
12904 }
12905
12906 /*
12907 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
12908 endless glitching due to it constantly running out of data.
12909 */
12910 if (isPlaybackDeviceStarted) {
12911 DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback;
12912 if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) {
12913 silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback;
12914 if (silentPaddingInBytes > lockSizeInBytesPlayback) {
12915 silentPaddingInBytes = lockSizeInBytesPlayback;
12916 }
12917
12918 #ifdef MA_DEBUG_OUTPUT
12919 printf("[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%d, silentPaddingInBytes=%d\n", availableBytesPlayback, silentPaddingInBytes);
12920 #endif
12921 }
12922 }
12923
12924 /* At this point we have a buffer for output. */
12925 if (silentPaddingInBytes > 0) {
12926 MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes);
12927 framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback;
12928 } else {
12929 ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed);
12930 ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback;
12931 void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback);
12932 void* pConvertedFramesOut = pMappedDeviceBufferPlayback;
12933
12934 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut);
12935 if (result != MA_SUCCESS) {
12936 break;
12937 }
12938
12939 outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut;
12940 framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut;
12941 }
12942
12943
12944 hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0);
12945 if (FAILED(hr)) {
12946 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
12947 break;
12948 }
12949
12950 virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback;
12951 if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) {
12952 virtualWriteCursorInBytesPlayback = 0;
12953 virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
12954 }
12955
12956 /*
12957 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
12958 a bit of a buffer to prevent the playback buffer from getting starved.
12959 */
12960 framesWrittenToPlaybackDevice += framesWrittenThisIteration;
12961 if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) {
12962 if (FAILED(ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING))) {
12963 ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
12964 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
12965 }
12966 isPlaybackDeviceStarted = MA_TRUE;
12967 }
12968
12969 if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) {
12970 break; /* We're finished with the output data.*/
12971 }
12972 }
12973
12974 if (clientCapturedFramesToProcess == 0) {
12975 break; /* We just consumed every input sample. */
12976 }
12977 }
12978
12979
12980 /* At this point we're done with the mapped portion of the capture buffer. */
12981 hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
12982 if (FAILED(hr)) {
12983 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
12984 }
12985 prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture);
12986 } break;
12987
12988
12989
12990 case ma_device_type_capture:
12991 {
12992 DWORD physicalCaptureCursorInBytes;
12993 DWORD physicalReadCursorInBytes;
12994 if (FAILED(ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes))) {
12995 return MA_ERROR;
12996 }
12997
12998 /* If the previous capture position is the same as the current position we need to wait a bit longer. */
12999 if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) {
13000 ma_sleep(waitTimeInMilliseconds);
13001 continue;
13002 }
13003
13004 /* Getting here means we have capture data available. */
13005 if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
13006 /* The capture position has not looped. This is the simple case. */
13007 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
13008 lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
13009 } else {
13010 /*
13011 The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
13012 do it again from the start.
13013 */
13014 if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
13015 /* Lock up to the end of the buffer. */
13016 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
13017 lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
13018 } else {
13019 /* Lock starting from the start of the buffer. */
13020 lockOffsetInBytesCapture = 0;
13021 lockSizeInBytesCapture = physicalReadCursorInBytes;
13022 }
13023 }
13024
13025 #ifdef MA_DEBUG_OUTPUT
13026 /*printf("[DirectSound] (Capture) physicalCaptureCursorInBytes=%d, physicalReadCursorInBytes=%d\n", physicalCaptureCursorInBytes, physicalReadCursorInBytes);*/
13027 /*printf("[DirectSound] (Capture) lockOffsetInBytesCapture=%d, lockSizeInBytesCapture=%d\n", lockOffsetInBytesCapture, lockSizeInBytesCapture);*/
13028 #endif
13029
13030 if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) {
13031 ma_sleep(waitTimeInMilliseconds);
13032 continue; /* Nothing is available in the capture buffer. */
13033 }
13034
13035 hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
13036 if (FAILED(hr)) {
13037 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_FAILED_TO_MAP_DEVICE_BUFFER);
13038 }
13039
13040 #ifdef MA_DEBUG_OUTPUT
13041 if (lockSizeInBytesCapture != mappedSizeInBytesCapture) {
13042 printf("[DirectSound] (Capture) lockSizeInBytesCapture=%d != mappedSizeInBytesCapture=%d\n", lockSizeInBytesCapture, mappedSizeInBytesCapture);
13043 }
13044 #endif
13045
13046 ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture);
13047
13048 hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
13049 if (FAILED(hr)) {
13050 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
13051 }
13052 prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture;
13053
13054 if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) {
13055 prevReadCursorInBytesCapture = 0;
13056 }
13057 } break;
13058
13059
13060
13061 case ma_device_type_playback:
13062 {
13063 DWORD availableBytesPlayback;
13064 DWORD physicalPlayCursorInBytes;
13065 DWORD physicalWriteCursorInBytes;
13066 if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
13067 break;
13068 }
13069
13070 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
13071 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
13072 }
13073 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
13074
13075 /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
13076 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
13077 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
13078 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
13079 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
13080 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
13081 } else {
13082 /* This is an error. */
13083 #ifdef MA_DEBUG_OUTPUT
13084 printf("[DirectSound] (Playback) WARNING: Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%d, virtualWriteCursorInBytes=%d.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
13085 #endif
13086 availableBytesPlayback = 0;
13087 }
13088 } else {
13089 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
13090 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
13091 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
13092 } else {
13093 /* This is an error. */
13094 #ifdef MA_DEBUG_OUTPUT
13095 printf("[DirectSound] (Playback) WARNING: Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%d, virtualWriteCursorInBytes=%d.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
13096 #endif
13097 availableBytesPlayback = 0;
13098 }
13099 }
13100
13101 #ifdef MA_DEBUG_OUTPUT
13102 /*printf("[DirectSound] (Playback) physicalPlayCursorInBytes=%d, availableBytesPlayback=%d\n", physicalPlayCursorInBytes, availableBytesPlayback);*/
13103 #endif
13104
13105 /* If there's no room available for writing we need to wait for more. */
13106 if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) {
13107 /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
13108 if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) {
13109 if (FAILED(ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING))) {
13110 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
13111 }
13112 isPlaybackDeviceStarted = MA_TRUE;
13113 } else {
13114 ma_sleep(waitTimeInMilliseconds);
13115 continue;
13116 }
13117 }
13118
13119 /* 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. */
13120 lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
13121 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
13122 /* Same loop iteration. Go up to the end of the buffer. */
13123 lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
13124 } else {
13125 /* Different loop iterations. Go up to the physical play cursor. */
13126 lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
13127 }
13128
13129 hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
13130 if (FAILED(hr)) {
13131 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_FAILED_TO_MAP_DEVICE_BUFFER);
13132 break;
13133 }
13134
13135 /* At this point we have a buffer for output. */
13136 ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback);
13137
13138 hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0);
13139 if (FAILED(hr)) {
13140 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
13141 break;
13142 }
13143
13144 virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback;
13145 if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) {
13146 virtualWriteCursorInBytesPlayback = 0;
13147 virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
13148 }
13149
13150 /*
13151 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
13152 a bit of a buffer to prevent the playback buffer from getting starved.
13153 */
13154 framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback;
13155 if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) {
13156 if (FAILED(ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING))) {
13157 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
13158 }
13159 isPlaybackDeviceStarted = MA_TRUE;
13160 }
13161 } break;
13162
13163
13164 default: return MA_INVALID_ARGS; /* Invalid device type. */
13165 }
13166
13167 if (result != MA_SUCCESS) {
13168 return result;
13169 }
13170 }
13171
13172 /* Getting here means the device is being stopped. */
13173 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
13174 if (FAILED(ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer))) {
13175 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
13176 }
13177 }
13178
13179 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
13180 /* 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. */
13181 if (isPlaybackDeviceStarted) {
13182 for (;;) {
13183 DWORD availableBytesPlayback = 0;
13184 DWORD physicalPlayCursorInBytes;
13185 DWORD physicalWriteCursorInBytes;
13186 if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
13187 break;
13188 }
13189
13190 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
13191 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
13192 }
13193 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
13194
13195 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
13196 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
13197 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
13198 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
13199 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
13200 } else {
13201 break;
13202 }
13203 } else {
13204 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
13205 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
13206 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
13207 } else {
13208 break;
13209 }
13210 }
13211
13212 if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) {
13213 break;
13214 }
13215
13216 ma_sleep(waitTimeInMilliseconds);
13217 }
13218 }
13219
13220 if (FAILED(ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer))) {
13221 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
13222 }
13223
13224 ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0);
13225 }
13226
13227 return MA_SUCCESS;
13228}
13229
13230static ma_result ma_context_uninit__dsound(ma_context* pContext)
13231{
13232 MA_ASSERT(pContext != NULL);
13233 MA_ASSERT(pContext->backend == ma_backend_dsound);
13234
13235 ma_dlclose(pContext, pContext->dsound.hDSoundDLL);
13236
13237 return MA_SUCCESS;
13238}
13239
13240static ma_result ma_context_init__dsound(const ma_context_config* pConfig, ma_context* pContext)
13241{
13242 MA_ASSERT(pContext != NULL);
13243
13244 (void)pConfig;
13245
13246 pContext->dsound.hDSoundDLL = ma_dlopen(pContext, "dsound.dll");
13247 if (pContext->dsound.hDSoundDLL == NULL) {
13248 return MA_API_NOT_FOUND;
13249 }
13250
13251 pContext->dsound.DirectSoundCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCreate");
13252 pContext->dsound.DirectSoundEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA");
13253 pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate");
13254 pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA");
13255
13256 pContext->onUninit = ma_context_uninit__dsound;
13257 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__dsound;
13258 pContext->onEnumDevices = ma_context_enumerate_devices__dsound;
13259 pContext->onGetDeviceInfo = ma_context_get_device_info__dsound;
13260 pContext->onDeviceInit = ma_device_init__dsound;
13261 pContext->onDeviceUninit = ma_device_uninit__dsound;
13262 pContext->onDeviceStart = NULL; /* Not used. Started in onDeviceMainLoop. */
13263 pContext->onDeviceStop = NULL; /* Not used. Stopped in onDeviceMainLoop. */
13264 pContext->onDeviceMainLoop = ma_device_main_loop__dsound;
13265
13266 return MA_SUCCESS;
13267}
13268#endif
13269
13270
13271
13272/******************************************************************************
13273
13274WinMM Backend
13275
13276******************************************************************************/
13277#ifdef MA_HAS_WINMM
13278
13279/*
13280Some older compilers don't have WAVEOUTCAPS2A and WAVEINCAPS2A, so we'll need to write this ourselves. These structures
13281are exactly the same as the older ones but they have a few GUIDs for manufacturer/product/name identification. I'm keeping
13282the names the same as the Win32 library for consistency, but namespaced to avoid naming conflicts with the Win32 version.
13283*/
13284typedef struct
13285{
13286 WORD wMid;
13287 WORD wPid;
13288 MMVERSION vDriverVersion;
13289 CHAR szPname[MAXPNAMELEN];
13290 DWORD dwFormats;
13291 WORD wChannels;
13292 WORD wReserved1;
13293 DWORD dwSupport;
13294 GUID ManufacturerGuid;
13295 GUID ProductGuid;
13296 GUID NameGuid;
13297} MA_WAVEOUTCAPS2A;
13298typedef struct
13299{
13300 WORD wMid;
13301 WORD wPid;
13302 MMVERSION vDriverVersion;
13303 CHAR szPname[MAXPNAMELEN];
13304 DWORD dwFormats;
13305 WORD wChannels;
13306 WORD wReserved1;
13307 GUID ManufacturerGuid;
13308 GUID ProductGuid;
13309 GUID NameGuid;
13310} MA_WAVEINCAPS2A;
13311
13312typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void);
13313typedef MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc);
13314typedef MMRESULT (WINAPI * MA_PFN_waveOutOpen)(LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
13315typedef MMRESULT (WINAPI * MA_PFN_waveOutClose)(HWAVEOUT hwo);
13316typedef MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
13317typedef MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
13318typedef MMRESULT (WINAPI * MA_PFN_waveOutWrite)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
13319typedef MMRESULT (WINAPI * MA_PFN_waveOutReset)(HWAVEOUT hwo);
13320typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void);
13321typedef MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic);
13322typedef MMRESULT (WINAPI * MA_PFN_waveInOpen)(LPHWAVEIN phwi, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
13323typedef MMRESULT (WINAPI * MA_PFN_waveInClose)(HWAVEIN hwi);
13324typedef MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
13325typedef MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
13326typedef MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
13327typedef MMRESULT (WINAPI * MA_PFN_waveInStart)(HWAVEIN hwi);
13328typedef MMRESULT (WINAPI * MA_PFN_waveInReset)(HWAVEIN hwi);
13329
13330static ma_result ma_result_from_MMRESULT(MMRESULT resultMM)
13331{
13332 switch (resultMM) {
13333 case MMSYSERR_NOERROR: return MA_SUCCESS;
13334 case MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS;
13335 case MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS;
13336 case MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY;
13337 case MMSYSERR_INVALFLAG: return MA_INVALID_ARGS;
13338 case MMSYSERR_INVALPARAM: return MA_INVALID_ARGS;
13339 case MMSYSERR_HANDLEBUSY: return MA_DEVICE_BUSY;
13340 case MMSYSERR_ERROR: return MA_ERROR;
13341 default: return MA_ERROR;
13342 }
13343}
13344
13345static char* ma_find_last_character(char* str, char ch)
13346{
13347 char* last;
13348
13349 if (str == NULL) {
13350 return NULL;
13351 }
13352
13353 last = NULL;
13354 while (*str != '\0') {
13355 if (*str == ch) {
13356 last = str;
13357 }
13358
13359 str += 1;
13360 }
13361
13362 return last;
13363}
13364
13365static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels)
13366{
13367 return periodSizeInFrames * ma_get_bytes_per_frame(format, channels);
13368}
13369
13370
13371/*
13372Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so
13373we can do things generically and typesafely. Names are being kept the same for consistency.
13374*/
13375typedef struct
13376{
13377 CHAR szPname[MAXPNAMELEN];
13378 DWORD dwFormats;
13379 WORD wChannels;
13380 GUID NameGuid;
13381} MA_WAVECAPSA;
13382
13383static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate)
13384{
13385 WORD bitsPerSample = 0;
13386 DWORD sampleRate = 0;
13387
13388 if (pBitsPerSample) {
13389 *pBitsPerSample = 0;
13390 }
13391 if (pSampleRate) {
13392 *pSampleRate = 0;
13393 }
13394
13395 if (channels == 1) {
13396 bitsPerSample = 16;
13397 if ((dwFormats & WAVE_FORMAT_48M16) != 0) {
13398 sampleRate = 48000;
13399 } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {
13400 sampleRate = 44100;
13401 } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {
13402 sampleRate = 22050;
13403 } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {
13404 sampleRate = 11025;
13405 } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {
13406 sampleRate = 96000;
13407 } else {
13408 bitsPerSample = 8;
13409 if ((dwFormats & WAVE_FORMAT_48M08) != 0) {
13410 sampleRate = 48000;
13411 } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {
13412 sampleRate = 44100;
13413 } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {
13414 sampleRate = 22050;
13415 } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {
13416 sampleRate = 11025;
13417 } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {
13418 sampleRate = 96000;
13419 } else {
13420 return MA_FORMAT_NOT_SUPPORTED;
13421 }
13422 }
13423 } else {
13424 bitsPerSample = 16;
13425 if ((dwFormats & WAVE_FORMAT_48S16) != 0) {
13426 sampleRate = 48000;
13427 } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {
13428 sampleRate = 44100;
13429 } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {
13430 sampleRate = 22050;
13431 } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {
13432 sampleRate = 11025;
13433 } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {
13434 sampleRate = 96000;
13435 } else {
13436 bitsPerSample = 8;
13437 if ((dwFormats & WAVE_FORMAT_48S08) != 0) {
13438 sampleRate = 48000;
13439 } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {
13440 sampleRate = 44100;
13441 } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {
13442 sampleRate = 22050;
13443 } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {
13444 sampleRate = 11025;
13445 } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {
13446 sampleRate = 96000;
13447 } else {
13448 return MA_FORMAT_NOT_SUPPORTED;
13449 }
13450 }
13451 }
13452
13453 if (pBitsPerSample) {
13454 *pBitsPerSample = bitsPerSample;
13455 }
13456 if (pSampleRate) {
13457 *pSampleRate = sampleRate;
13458 }
13459
13460 return MA_SUCCESS;
13461}
13462
13463static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, WAVEFORMATEX* pWF)
13464{
13465 MA_ASSERT(pWF != NULL);
13466
13467 MA_ZERO_OBJECT(pWF);
13468 pWF->cbSize = sizeof(*pWF);
13469 pWF->wFormatTag = WAVE_FORMAT_PCM;
13470 pWF->nChannels = (WORD)channels;
13471 if (pWF->nChannels > 2) {
13472 pWF->nChannels = 2;
13473 }
13474
13475 if (channels == 1) {
13476 pWF->wBitsPerSample = 16;
13477 if ((dwFormats & WAVE_FORMAT_48M16) != 0) {
13478 pWF->nSamplesPerSec = 48000;
13479 } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {
13480 pWF->nSamplesPerSec = 44100;
13481 } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {
13482 pWF->nSamplesPerSec = 22050;
13483 } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {
13484 pWF->nSamplesPerSec = 11025;
13485 } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {
13486 pWF->nSamplesPerSec = 96000;
13487 } else {
13488 pWF->wBitsPerSample = 8;
13489 if ((dwFormats & WAVE_FORMAT_48M08) != 0) {
13490 pWF->nSamplesPerSec = 48000;
13491 } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {
13492 pWF->nSamplesPerSec = 44100;
13493 } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {
13494 pWF->nSamplesPerSec = 22050;
13495 } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {
13496 pWF->nSamplesPerSec = 11025;
13497 } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {
13498 pWF->nSamplesPerSec = 96000;
13499 } else {
13500 return MA_FORMAT_NOT_SUPPORTED;
13501 }
13502 }
13503 } else {
13504 pWF->wBitsPerSample = 16;
13505 if ((dwFormats & WAVE_FORMAT_48S16) != 0) {
13506 pWF->nSamplesPerSec = 48000;
13507 } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {
13508 pWF->nSamplesPerSec = 44100;
13509 } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {
13510 pWF->nSamplesPerSec = 22050;
13511 } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {
13512 pWF->nSamplesPerSec = 11025;
13513 } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {
13514 pWF->nSamplesPerSec = 96000;
13515 } else {
13516 pWF->wBitsPerSample = 8;
13517 if ((dwFormats & WAVE_FORMAT_48S08) != 0) {
13518 pWF->nSamplesPerSec = 48000;
13519 } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {
13520 pWF->nSamplesPerSec = 44100;
13521 } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {
13522 pWF->nSamplesPerSec = 22050;
13523 } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {
13524 pWF->nSamplesPerSec = 11025;
13525 } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {
13526 pWF->nSamplesPerSec = 96000;
13527 } else {
13528 return MA_FORMAT_NOT_SUPPORTED;
13529 }
13530 }
13531 }
13532
13533 pWF->nBlockAlign = (pWF->nChannels * pWF->wBitsPerSample) / 8;
13534 pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec;
13535
13536 return MA_SUCCESS;
13537}
13538
13539static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo)
13540{
13541 WORD bitsPerSample;
13542 DWORD sampleRate;
13543 ma_result result;
13544
13545 MA_ASSERT(pContext != NULL);
13546 MA_ASSERT(pCaps != NULL);
13547 MA_ASSERT(pDeviceInfo != NULL);
13548
13549 /*
13550 Name / Description
13551
13552 Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking
13553 situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try
13554 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.
13555 */
13556
13557 /* Set the default to begin with. */
13558 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1);
13559
13560 /*
13561 Now try the registry. There's a few things to consider here:
13562 - The name GUID can be null, in which we case we just need to stick to the original 31 characters.
13563 - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters.
13564 - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The
13565 problem, however is that WASAPI and DirectSound use "<component> (<name>)" format (such as "Speakers (High Definition Audio)"),
13566 but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to
13567 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
13568 name, and then concatenate the name from the registry.
13569 */
13570 if (!ma_is_guid_equal(&pCaps->NameGuid, &MA_GUID_NULL)) {
13571 wchar_t guidStrW[256];
13572 if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) {
13573 char guidStr[256];
13574 char keyStr[1024];
13575 HKEY hKey;
13576
13577 WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE);
13578
13579 ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\");
13580 ma_strcat_s(keyStr, sizeof(keyStr), guidStr);
13581
13582 if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
13583 BYTE nameFromReg[512];
13584 DWORD nameFromRegSize = sizeof(nameFromReg);
13585 result = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize);
13586 ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey);
13587
13588 if (result == ERROR_SUCCESS) {
13589 /* We have the value from the registry, so now we need to construct the name string. */
13590 char name[1024];
13591 if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) {
13592 char* nameBeg = ma_find_last_character(name, '(');
13593 if (nameBeg != NULL) {
13594 size_t leadingLen = (nameBeg - name);
13595 ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1);
13596
13597 /* The closing ")", if it can fit. */
13598 if (leadingLen + nameFromRegSize < sizeof(name)-1) {
13599 ma_strcat_s(name, sizeof(name), ")");
13600 }
13601
13602 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1);
13603 }
13604 }
13605 }
13606 }
13607 }
13608 }
13609
13610
13611 result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate);
13612 if (result != MA_SUCCESS) {
13613 return result;
13614 }
13615
13616 pDeviceInfo->minChannels = pCaps->wChannels;
13617 pDeviceInfo->maxChannels = pCaps->wChannels;
13618 pDeviceInfo->minSampleRate = sampleRate;
13619 pDeviceInfo->maxSampleRate = sampleRate;
13620 pDeviceInfo->formatCount = 1;
13621 if (bitsPerSample == 8) {
13622 pDeviceInfo->formats[0] = ma_format_u8;
13623 } else if (bitsPerSample == 16) {
13624 pDeviceInfo->formats[0] = ma_format_s16;
13625 } else if (bitsPerSample == 24) {
13626 pDeviceInfo->formats[0] = ma_format_s24;
13627 } else if (bitsPerSample == 32) {
13628 pDeviceInfo->formats[0] = ma_format_s32;
13629 } else {
13630 return MA_FORMAT_NOT_SUPPORTED;
13631 }
13632
13633 return MA_SUCCESS;
13634}
13635
13636static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo)
13637{
13638 MA_WAVECAPSA caps;
13639
13640 MA_ASSERT(pContext != NULL);
13641 MA_ASSERT(pCaps != NULL);
13642 MA_ASSERT(pDeviceInfo != NULL);
13643
13644 MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
13645 caps.dwFormats = pCaps->dwFormats;
13646 caps.wChannels = pCaps->wChannels;
13647 caps.NameGuid = pCaps->NameGuid;
13648 return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
13649}
13650
13651static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo)
13652{
13653 MA_WAVECAPSA caps;
13654
13655 MA_ASSERT(pContext != NULL);
13656 MA_ASSERT(pCaps != NULL);
13657 MA_ASSERT(pDeviceInfo != NULL);
13658
13659 MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
13660 caps.dwFormats = pCaps->dwFormats;
13661 caps.wChannels = pCaps->wChannels;
13662 caps.NameGuid = pCaps->NameGuid;
13663 return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
13664}
13665
13666
13667static ma_bool32 ma_context_is_device_id_equal__winmm(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
13668{
13669 MA_ASSERT(pContext != NULL);
13670 MA_ASSERT(pID0 != NULL);
13671 MA_ASSERT(pID1 != NULL);
13672 (void)pContext;
13673
13674 return pID0->winmm == pID1->winmm;
13675}
13676
13677static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
13678{
13679 UINT playbackDeviceCount;
13680 UINT captureDeviceCount;
13681 UINT iPlaybackDevice;
13682 UINT iCaptureDevice;
13683
13684 MA_ASSERT(pContext != NULL);
13685 MA_ASSERT(callback != NULL);
13686
13687 /* Playback. */
13688 playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)();
13689 for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) {
13690 MMRESULT result;
13691 MA_WAVEOUTCAPS2A caps;
13692
13693 MA_ZERO_OBJECT(&caps);
13694
13695 result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (WAVEOUTCAPSA*)&caps, sizeof(caps));
13696 if (result == MMSYSERR_NOERROR) {
13697 ma_device_info deviceInfo;
13698
13699 MA_ZERO_OBJECT(&deviceInfo);
13700 deviceInfo.id.winmm = iPlaybackDevice;
13701
13702 if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
13703 ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
13704 if (cbResult == MA_FALSE) {
13705 return MA_SUCCESS; /* Enumeration was stopped. */
13706 }
13707 }
13708 }
13709 }
13710
13711 /* Capture. */
13712 captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)();
13713 for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) {
13714 MMRESULT result;
13715 MA_WAVEINCAPS2A caps;
13716
13717 MA_ZERO_OBJECT(&caps);
13718
13719 result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (WAVEINCAPSA*)&caps, sizeof(caps));
13720 if (result == MMSYSERR_NOERROR) {
13721 ma_device_info deviceInfo;
13722
13723 MA_ZERO_OBJECT(&deviceInfo);
13724 deviceInfo.id.winmm = iCaptureDevice;
13725
13726 if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
13727 ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
13728 if (cbResult == MA_FALSE) {
13729 return MA_SUCCESS; /* Enumeration was stopped. */
13730 }
13731 }
13732 }
13733 }
13734
13735 return MA_SUCCESS;
13736}
13737
13738static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
13739{
13740 UINT winMMDeviceID;
13741
13742 MA_ASSERT(pContext != NULL);
13743
13744 if (shareMode == ma_share_mode_exclusive) {
13745 return MA_SHARE_MODE_NOT_SUPPORTED;
13746 }
13747
13748 winMMDeviceID = 0;
13749 if (pDeviceID != NULL) {
13750 winMMDeviceID = (UINT)pDeviceID->winmm;
13751 }
13752
13753 pDeviceInfo->id.winmm = winMMDeviceID;
13754
13755 if (deviceType == ma_device_type_playback) {
13756 MMRESULT result;
13757 MA_WAVEOUTCAPS2A caps;
13758
13759 MA_ZERO_OBJECT(&caps);
13760
13761 result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (WAVEOUTCAPSA*)&caps, sizeof(caps));
13762 if (result == MMSYSERR_NOERROR) {
13763 return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo);
13764 }
13765 } else {
13766 MMRESULT result;
13767 MA_WAVEINCAPS2A caps;
13768
13769 MA_ZERO_OBJECT(&caps);
13770
13771 result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (WAVEINCAPSA*)&caps, sizeof(caps));
13772 if (result == MMSYSERR_NOERROR) {
13773 return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo);
13774 }
13775 }
13776
13777 return MA_NO_DEVICE;
13778}
13779
13780
13781static void ma_device_uninit__winmm(ma_device* pDevice)
13782{
13783 MA_ASSERT(pDevice != NULL);
13784
13785 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
13786 ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture);
13787 CloseHandle((HANDLE)pDevice->winmm.hEventCapture);
13788 }
13789
13790 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
13791 ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
13792 ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
13793 CloseHandle((HANDLE)pDevice->winmm.hEventPlayback);
13794 }
13795
13796 ma__free_from_callbacks(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);
13797
13798 MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */
13799}
13800
13801static ma_result ma_device_init__winmm(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
13802{
13803 const char* errorMsg = "";
13804 ma_result errorCode = MA_ERROR;
13805 ma_result result = MA_SUCCESS;
13806 ma_uint32 heapSize;
13807 UINT winMMDeviceIDPlayback = 0;
13808 UINT winMMDeviceIDCapture = 0;
13809 ma_uint32 periodSizeInMilliseconds;
13810
13811 MA_ASSERT(pDevice != NULL);
13812 MA_ZERO_OBJECT(&pDevice->winmm);
13813
13814 if (pConfig->deviceType == ma_device_type_loopback) {
13815 return MA_DEVICE_TYPE_NOT_SUPPORTED;
13816 }
13817
13818 /* No exlusive mode with WinMM. */
13819 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
13820 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
13821 return MA_SHARE_MODE_NOT_SUPPORTED;
13822 }
13823
13824 periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
13825 if (periodSizeInMilliseconds == 0) {
13826 periodSizeInMilliseconds = ma_calculate_buffer_size_in_milliseconds_from_frames(pConfig->periodSizeInFrames, pConfig->sampleRate);
13827 }
13828
13829 /* WinMM has horrible latency. */
13830 if (pDevice->usingDefaultBufferSize) {
13831 if (pConfig->performanceProfile == ma_performance_profile_low_latency) {
13832 periodSizeInMilliseconds = 40;
13833 } else {
13834 periodSizeInMilliseconds = 400;
13835 }
13836 }
13837
13838
13839 if (pConfig->playback.pDeviceID != NULL) {
13840 winMMDeviceIDPlayback = (UINT)pConfig->playback.pDeviceID->winmm;
13841 }
13842 if (pConfig->capture.pDeviceID != NULL) {
13843 winMMDeviceIDCapture = (UINT)pConfig->capture.pDeviceID->winmm;
13844 }
13845
13846 /* The capture device needs to be initialized first. */
13847 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
13848 WAVEINCAPSA caps;
13849 WAVEFORMATEX wf;
13850 MMRESULT resultMM;
13851
13852 /* We use an event to know when a new fragment needs to be enqueued. */
13853 pDevice->winmm.hEventCapture = (ma_handle)CreateEvent(NULL, TRUE, TRUE, NULL);
13854 if (pDevice->winmm.hEventCapture == NULL) {
13855 errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = MA_FAILED_TO_CREATE_EVENT;
13856 goto on_error;
13857 }
13858
13859 /* The format should be based on the device's actual format. */
13860 if (((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MMSYSERR_NOERROR) {
13861 errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
13862 goto on_error;
13863 }
13864
13865 result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
13866 if (result != MA_SUCCESS) {
13867 errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
13868 goto on_error;
13869 }
13870
13871 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);
13872 if (resultMM != MMSYSERR_NOERROR) {
13873 errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
13874 goto on_error;
13875 }
13876
13877 pDevice->capture.internalFormat = ma_format_from_WAVEFORMATEX(&wf);
13878 pDevice->capture.internalChannels = wf.nChannels;
13879 pDevice->capture.internalSampleRate = wf.nSamplesPerSec;
13880 ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
13881 pDevice->capture.internalPeriods = pConfig->periods;
13882 pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, pDevice->capture.internalSampleRate);
13883 }
13884
13885 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
13886 WAVEOUTCAPSA caps;
13887 WAVEFORMATEX wf;
13888 MMRESULT resultMM;
13889
13890 /* We use an event to know when a new fragment needs to be enqueued. */
13891 pDevice->winmm.hEventPlayback = (ma_handle)CreateEvent(NULL, TRUE, TRUE, NULL);
13892 if (pDevice->winmm.hEventPlayback == NULL) {
13893 errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = MA_FAILED_TO_CREATE_EVENT;
13894 goto on_error;
13895 }
13896
13897 /* The format should be based on the device's actual format. */
13898 if (((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MMSYSERR_NOERROR) {
13899 errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
13900 goto on_error;
13901 }
13902
13903 result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
13904 if (result != MA_SUCCESS) {
13905 errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
13906 goto on_error;
13907 }
13908
13909 resultMM = ((MA_PFN_waveOutOpen)pContext->winmm.waveOutOpen)((LPHWAVEOUT)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC);
13910 if (resultMM != MMSYSERR_NOERROR) {
13911 errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
13912 goto on_error;
13913 }
13914
13915 pDevice->playback.internalFormat = ma_format_from_WAVEFORMATEX(&wf);
13916 pDevice->playback.internalChannels = wf.nChannels;
13917 pDevice->playback.internalSampleRate = wf.nSamplesPerSec;
13918 ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
13919 pDevice->playback.internalPeriods = pConfig->periods;
13920 pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, pDevice->playback.internalSampleRate);
13921 }
13922
13923 /*
13924 The heap allocated data is allocated like so:
13925
13926 [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer]
13927 */
13928 heapSize = 0;
13929 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
13930 heapSize += sizeof(WAVEHDR)*pDevice->capture.internalPeriods + (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
13931 }
13932 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
13933 heapSize += sizeof(WAVEHDR)*pDevice->playback.internalPeriods + (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
13934 }
13935
13936 pDevice->winmm._pHeapData = (ma_uint8*)ma__calloc_from_callbacks(heapSize, &pContext->allocationCallbacks);
13937 if (pDevice->winmm._pHeapData == NULL) {
13938 errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY;
13939 goto on_error;
13940 }
13941
13942 MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize);
13943
13944 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
13945 ma_uint32 iPeriod;
13946
13947 if (pConfig->deviceType == ma_device_type_capture) {
13948 pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
13949 pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods));
13950 } else {
13951 pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
13952 pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods + pDevice->playback.internalPeriods));
13953 }
13954
13955 /* Prepare headers. */
13956 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
13957 ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalFormat, pDevice->capture.internalChannels);
13958
13959 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod));
13960 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes;
13961 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L;
13962 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L;
13963 ((MA_PFN_waveInPrepareHeader)pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
13964
13965 /*
13966 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
13967 it's unlocked and available for writing. A value of 1 means it's locked.
13968 */
13969 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0;
13970 }
13971 }
13972 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
13973 ma_uint32 iPeriod;
13974
13975 if (pConfig->deviceType == ma_device_type_playback) {
13976 pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData;
13977 pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*pDevice->playback.internalPeriods);
13978 } else {
13979 pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods));
13980 pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods + pDevice->playback.internalPeriods)) + (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
13981 }
13982
13983 /* Prepare headers. */
13984 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
13985 ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalFormat, pDevice->playback.internalChannels);
13986
13987 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod));
13988 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes;
13989 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L;
13990 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L;
13991 ((MA_PFN_waveOutPrepareHeader)pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR));
13992
13993 /*
13994 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
13995 it's unlocked and available for writing. A value of 1 means it's locked.
13996 */
13997 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0;
13998 }
13999 }
14000
14001 return MA_SUCCESS;
14002
14003on_error:
14004 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
14005 if (pDevice->winmm.pWAVEHDRCapture != NULL) {
14006 ma_uint32 iPeriod;
14007 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
14008 ((MA_PFN_waveInUnprepareHeader)pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
14009 }
14010 }
14011
14012 ((MA_PFN_waveInClose)pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture);
14013 }
14014
14015 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
14016 if (pDevice->winmm.pWAVEHDRCapture != NULL) {
14017 ma_uint32 iPeriod;
14018 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
14019 ((MA_PFN_waveOutUnprepareHeader)pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR));
14020 }
14021 }
14022
14023 ((MA_PFN_waveOutClose)pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
14024 }
14025
14026 ma__free_from_callbacks(pDevice->winmm._pHeapData, &pContext->allocationCallbacks);
14027 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, errorMsg, errorCode);
14028}
14029
14030static ma_result ma_device_stop__winmm(ma_device* pDevice)
14031{
14032 MMRESULT resultMM;
14033
14034 MA_ASSERT(pDevice != NULL);
14035
14036 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
14037 if (pDevice->winmm.hDeviceCapture == NULL) {
14038 return MA_INVALID_ARGS;
14039 }
14040
14041 resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((HWAVEIN)pDevice->winmm.hDeviceCapture);
14042 if (resultMM != MMSYSERR_NOERROR) {
14043 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset capture device.", ma_result_from_MMRESULT(resultMM));
14044 }
14045 }
14046
14047 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
14048 ma_uint32 iPeriod;
14049 WAVEHDR* pWAVEHDR;
14050
14051 if (pDevice->winmm.hDevicePlayback == NULL) {
14052 return MA_INVALID_ARGS;
14053 }
14054
14055 /* 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. */
14056 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
14057 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) {
14058 if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */
14059 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
14060 break; /* An error occurred so just abandon ship and stop the device without draining. */
14061 }
14062
14063 pWAVEHDR[iPeriod].dwUser = 0;
14064 }
14065 }
14066
14067 resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
14068 if (resultMM != MMSYSERR_NOERROR) {
14069 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset playback device.", ma_result_from_MMRESULT(resultMM));
14070 }
14071 }
14072
14073 return MA_SUCCESS;
14074}
14075
14076static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
14077{
14078 ma_result result = MA_SUCCESS;
14079 MMRESULT resultMM;
14080 ma_uint32 totalFramesWritten;
14081 WAVEHDR* pWAVEHDR;
14082
14083 MA_ASSERT(pDevice != NULL);
14084 MA_ASSERT(pPCMFrames != NULL);
14085
14086 if (pFramesWritten != NULL) {
14087 *pFramesWritten = 0;
14088 }
14089
14090 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
14091
14092 /* Keep processing as much data as possible. */
14093 totalFramesWritten = 0;
14094 while (totalFramesWritten < frameCount) {
14095 /* If the current header has some space available we need to write part of it. */
14096 if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */
14097 /*
14098 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
14099 write it out and move on to the next iteration.
14100 */
14101 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
14102 ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback;
14103
14104 ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten));
14105 const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf);
14106 void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf);
14107 MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
14108
14109 pDevice->winmm.headerFramesConsumedPlayback += framesToCopy;
14110 totalFramesWritten += framesToCopy;
14111
14112 /* If we've consumed the buffer entirely we need to write it out to the device. */
14113 if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) {
14114 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */
14115 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
14116
14117 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
14118 ResetEvent((HANDLE)pDevice->winmm.hEventPlayback);
14119
14120 /* The device will be started here. */
14121 resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(WAVEHDR));
14122 if (resultMM != MMSYSERR_NOERROR) {
14123 result = ma_result_from_MMRESULT(resultMM);
14124 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed.", result);
14125 break;
14126 }
14127
14128 /* Make sure we move to the next header. */
14129 pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods;
14130 pDevice->winmm.headerFramesConsumedPlayback = 0;
14131 }
14132
14133 /* If at this point we have consumed the entire input buffer we can return. */
14134 MA_ASSERT(totalFramesWritten <= frameCount);
14135 if (totalFramesWritten == frameCount) {
14136 break;
14137 }
14138
14139 /* Getting here means there's more to process. */
14140 continue;
14141 }
14142
14143 /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */
14144 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
14145 result = MA_ERROR;
14146 break;
14147 }
14148
14149 /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
14150 if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & WHDR_DONE) != 0) {
14151 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */
14152 pDevice->winmm.headerFramesConsumedPlayback = 0;
14153 }
14154
14155 /* If the device has been stopped we need to break. */
14156 if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
14157 break;
14158 }
14159 }
14160
14161 if (pFramesWritten != NULL) {
14162 *pFramesWritten = totalFramesWritten;
14163 }
14164
14165 return result;
14166}
14167
14168static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
14169{
14170 ma_result result = MA_SUCCESS;
14171 MMRESULT resultMM;
14172 ma_uint32 totalFramesRead;
14173 WAVEHDR* pWAVEHDR;
14174
14175 MA_ASSERT(pDevice != NULL);
14176 MA_ASSERT(pPCMFrames != NULL);
14177
14178 if (pFramesRead != NULL) {
14179 *pFramesRead = 0;
14180 }
14181
14182 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
14183
14184 /* Keep processing as much data as possible. */
14185 totalFramesRead = 0;
14186 while (totalFramesRead < frameCount) {
14187 /* If the current header has some space available we need to write part of it. */
14188 if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */
14189 /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */
14190 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
14191 ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture;
14192
14193 ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead));
14194 const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf);
14195 void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf);
14196 MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
14197
14198 pDevice->winmm.headerFramesConsumedCapture += framesToCopy;
14199 totalFramesRead += framesToCopy;
14200
14201 /* If we've consumed the buffer entirely we need to add it back to the device. */
14202 if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) {
14203 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */
14204 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
14205
14206 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
14207 ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
14208
14209 /* The device will be started here. */
14210 resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(WAVEHDR));
14211 if (resultMM != MMSYSERR_NOERROR) {
14212 result = ma_result_from_MMRESULT(resultMM);
14213 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed.", result);
14214 break;
14215 }
14216
14217 /* Make sure we move to the next header. */
14218 pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods;
14219 pDevice->winmm.headerFramesConsumedCapture = 0;
14220 }
14221
14222 /* If at this point we have filled the entire input buffer we can return. */
14223 MA_ASSERT(totalFramesRead <= frameCount);
14224 if (totalFramesRead == frameCount) {
14225 break;
14226 }
14227
14228 /* Getting here means there's more to process. */
14229 continue;
14230 }
14231
14232 /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */
14233 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
14234 result = MA_ERROR;
14235 break;
14236 }
14237
14238 /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
14239 if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & WHDR_DONE) != 0) {
14240 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */
14241 pDevice->winmm.headerFramesConsumedCapture = 0;
14242 }
14243
14244 /* If the device has been stopped we need to break. */
14245 if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
14246 break;
14247 }
14248 }
14249
14250 if (pFramesRead != NULL) {
14251 *pFramesRead = totalFramesRead;
14252 }
14253
14254 return result;
14255}
14256
14257static ma_result ma_device_main_loop__winmm(ma_device* pDevice)
14258{
14259 ma_result result = MA_SUCCESS;
14260 ma_bool32 exitLoop = MA_FALSE;
14261
14262 MA_ASSERT(pDevice != NULL);
14263
14264 /* The capture device needs to be started immediately. */
14265 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
14266 MMRESULT resultMM;
14267 WAVEHDR* pWAVEHDR;
14268 ma_uint32 iPeriod;
14269
14270 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
14271
14272 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
14273 ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
14274
14275 /* 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. */
14276 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
14277 resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
14278 if (resultMM != MMSYSERR_NOERROR) {
14279 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));
14280 }
14281
14282 /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */
14283 pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */
14284 }
14285
14286 /* Capture devices need to be explicitly started, unlike playback devices. */
14287 resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((HWAVEIN)pDevice->winmm.hDeviceCapture);
14288 if (resultMM != MMSYSERR_NOERROR) {
14289 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device.", ma_result_from_MMRESULT(resultMM));
14290 }
14291 }
14292
14293
14294 while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
14295 switch (pDevice->type)
14296 {
14297 case ma_device_type_duplex:
14298 {
14299 /* The process is: device_read -> convert -> callback -> convert -> device_write */
14300 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
14301 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
14302
14303 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
14304 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
14305 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
14306 ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
14307 ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
14308 ma_uint32 capturedDeviceFramesRemaining;
14309 ma_uint32 capturedDeviceFramesProcessed;
14310 ma_uint32 capturedDeviceFramesToProcess;
14311 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
14312 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
14313 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
14314 }
14315
14316 result = ma_device_read__winmm(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
14317 if (result != MA_SUCCESS) {
14318 exitLoop = MA_TRUE;
14319 break;
14320 }
14321
14322 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
14323 capturedDeviceFramesProcessed = 0;
14324
14325 for (;;) {
14326 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
14327 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
14328 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
14329 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
14330 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
14331 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
14332 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
14333
14334 /* Convert capture data from device format to client format. */
14335 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
14336 if (result != MA_SUCCESS) {
14337 break;
14338 }
14339
14340 /*
14341 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
14342 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
14343 */
14344 if (capturedClientFramesToProcessThisIteration == 0) {
14345 break;
14346 }
14347
14348 ma_device__on_data(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
14349
14350 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
14351 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
14352
14353 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
14354 for (;;) {
14355 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
14356 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
14357 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
14358 if (result != MA_SUCCESS) {
14359 break;
14360 }
14361
14362 result = ma_device_write__winmm(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
14363 if (result != MA_SUCCESS) {
14364 exitLoop = MA_TRUE;
14365 break;
14366 }
14367
14368 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
14369 if (capturedClientFramesToProcessThisIteration == 0) {
14370 break;
14371 }
14372 }
14373
14374 /* In case an error happened from ma_device_write__winmm()... */
14375 if (result != MA_SUCCESS) {
14376 exitLoop = MA_TRUE;
14377 break;
14378 }
14379 }
14380
14381 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
14382 }
14383 } break;
14384
14385 case ma_device_type_capture:
14386 {
14387 /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
14388 ma_uint8 intermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
14389 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
14390 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
14391 ma_uint32 framesReadThisPeriod = 0;
14392 while (framesReadThisPeriod < periodSizeInFrames) {
14393 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
14394 ma_uint32 framesProcessed;
14395 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
14396 if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
14397 framesToReadThisIteration = intermediaryBufferSizeInFrames;
14398 }
14399
14400 result = ma_device_read__winmm(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
14401 if (result != MA_SUCCESS) {
14402 exitLoop = MA_TRUE;
14403 break;
14404 }
14405
14406 ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
14407
14408 framesReadThisPeriod += framesProcessed;
14409 }
14410 } break;
14411
14412 case ma_device_type_playback:
14413 {
14414 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
14415 ma_uint8 intermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
14416 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
14417 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
14418 ma_uint32 framesWrittenThisPeriod = 0;
14419 while (framesWrittenThisPeriod < periodSizeInFrames) {
14420 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
14421 ma_uint32 framesProcessed;
14422 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
14423 if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
14424 framesToWriteThisIteration = intermediaryBufferSizeInFrames;
14425 }
14426
14427 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
14428
14429 result = ma_device_write__winmm(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
14430 if (result != MA_SUCCESS) {
14431 exitLoop = MA_TRUE;
14432 break;
14433 }
14434
14435 framesWrittenThisPeriod += framesProcessed;
14436 }
14437 } break;
14438
14439 /* To silence a warning. Will never hit this. */
14440 case ma_device_type_loopback:
14441 default: break;
14442 }
14443 }
14444
14445
14446 /* Here is where the device is started. */
14447 ma_device_stop__winmm(pDevice);
14448
14449 return result;
14450}
14451
14452static ma_result ma_context_uninit__winmm(ma_context* pContext)
14453{
14454 MA_ASSERT(pContext != NULL);
14455 MA_ASSERT(pContext->backend == ma_backend_winmm);
14456
14457 ma_dlclose(pContext, pContext->winmm.hWinMM);
14458 return MA_SUCCESS;
14459}
14460
14461static ma_result ma_context_init__winmm(const ma_context_config* pConfig, ma_context* pContext)
14462{
14463 MA_ASSERT(pContext != NULL);
14464
14465 (void)pConfig;
14466
14467 pContext->winmm.hWinMM = ma_dlopen(pContext, "winmm.dll");
14468 if (pContext->winmm.hWinMM == NULL) {
14469 return MA_NO_BACKEND;
14470 }
14471
14472 pContext->winmm.waveOutGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetNumDevs");
14473 pContext->winmm.waveOutGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetDevCapsA");
14474 pContext->winmm.waveOutOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutOpen");
14475 pContext->winmm.waveOutClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutClose");
14476 pContext->winmm.waveOutPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutPrepareHeader");
14477 pContext->winmm.waveOutUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutUnprepareHeader");
14478 pContext->winmm.waveOutWrite = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutWrite");
14479 pContext->winmm.waveOutReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutReset");
14480 pContext->winmm.waveInGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetNumDevs");
14481 pContext->winmm.waveInGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetDevCapsA");
14482 pContext->winmm.waveInOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInOpen");
14483 pContext->winmm.waveInClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInClose");
14484 pContext->winmm.waveInPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInPrepareHeader");
14485 pContext->winmm.waveInUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInUnprepareHeader");
14486 pContext->winmm.waveInAddBuffer = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInAddBuffer");
14487 pContext->winmm.waveInStart = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInStart");
14488 pContext->winmm.waveInReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInReset");
14489
14490 pContext->onUninit = ma_context_uninit__winmm;
14491 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__winmm;
14492 pContext->onEnumDevices = ma_context_enumerate_devices__winmm;
14493 pContext->onGetDeviceInfo = ma_context_get_device_info__winmm;
14494 pContext->onDeviceInit = ma_device_init__winmm;
14495 pContext->onDeviceUninit = ma_device_uninit__winmm;
14496 pContext->onDeviceStart = NULL; /* Not used with synchronous backends. */
14497 pContext->onDeviceStop = NULL; /* Not used with synchronous backends. */
14498 pContext->onDeviceMainLoop = ma_device_main_loop__winmm;
14499
14500 return MA_SUCCESS;
14501}
14502#endif
14503
14504
14505
14506
14507/******************************************************************************
14508
14509ALSA Backend
14510
14511******************************************************************************/
14512#ifdef MA_HAS_ALSA
14513
14514#ifdef MA_NO_RUNTIME_LINKING
14515#include <alsa/asoundlib.h>
14516typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t;
14517typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t;
14518typedef snd_pcm_stream_t ma_snd_pcm_stream_t;
14519typedef snd_pcm_format_t ma_snd_pcm_format_t;
14520typedef snd_pcm_access_t ma_snd_pcm_access_t;
14521typedef snd_pcm_t ma_snd_pcm_t;
14522typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
14523typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
14524typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
14525typedef snd_pcm_info_t ma_snd_pcm_info_t;
14526typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t;
14527typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t;
14528
14529/* snd_pcm_stream_t */
14530#define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK
14531#define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE
14532
14533/* snd_pcm_format_t */
14534#define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN
14535#define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8
14536#define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE
14537#define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE
14538#define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE
14539#define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE
14540#define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE
14541#define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE
14542#define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE
14543#define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE
14544#define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE
14545#define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE
14546#define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW
14547#define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW
14548#define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE
14549#define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE
14550
14551/* ma_snd_pcm_access_t */
14552#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED
14553#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED
14554#define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX
14555#define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED
14556#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED
14557
14558/* Channel positions. */
14559#define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN
14560#define MA_SND_CHMAP_NA SND_CHMAP_NA
14561#define MA_SND_CHMAP_MONO SND_CHMAP_MONO
14562#define MA_SND_CHMAP_FL SND_CHMAP_FL
14563#define MA_SND_CHMAP_FR SND_CHMAP_FR
14564#define MA_SND_CHMAP_RL SND_CHMAP_RL
14565#define MA_SND_CHMAP_RR SND_CHMAP_RR
14566#define MA_SND_CHMAP_FC SND_CHMAP_FC
14567#define MA_SND_CHMAP_LFE SND_CHMAP_LFE
14568#define MA_SND_CHMAP_SL SND_CHMAP_SL
14569#define MA_SND_CHMAP_SR SND_CHMAP_SR
14570#define MA_SND_CHMAP_RC SND_CHMAP_RC
14571#define MA_SND_CHMAP_FLC SND_CHMAP_FLC
14572#define MA_SND_CHMAP_FRC SND_CHMAP_FRC
14573#define MA_SND_CHMAP_RLC SND_CHMAP_RLC
14574#define MA_SND_CHMAP_RRC SND_CHMAP_RRC
14575#define MA_SND_CHMAP_FLW SND_CHMAP_FLW
14576#define MA_SND_CHMAP_FRW SND_CHMAP_FRW
14577#define MA_SND_CHMAP_FLH SND_CHMAP_FLH
14578#define MA_SND_CHMAP_FCH SND_CHMAP_FCH
14579#define MA_SND_CHMAP_FRH SND_CHMAP_FRH
14580#define MA_SND_CHMAP_TC SND_CHMAP_TC
14581#define MA_SND_CHMAP_TFL SND_CHMAP_TFL
14582#define MA_SND_CHMAP_TFR SND_CHMAP_TFR
14583#define MA_SND_CHMAP_TFC SND_CHMAP_TFC
14584#define MA_SND_CHMAP_TRL SND_CHMAP_TRL
14585#define MA_SND_CHMAP_TRR SND_CHMAP_TRR
14586#define MA_SND_CHMAP_TRC SND_CHMAP_TRC
14587#define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC
14588#define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC
14589#define MA_SND_CHMAP_TSL SND_CHMAP_TSL
14590#define MA_SND_CHMAP_TSR SND_CHMAP_TSR
14591#define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE
14592#define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE
14593#define MA_SND_CHMAP_BC SND_CHMAP_BC
14594#define MA_SND_CHMAP_BLC SND_CHMAP_BLC
14595#define MA_SND_CHMAP_BRC SND_CHMAP_BRC
14596
14597/* Open mode flags. */
14598#define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE
14599#define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS
14600#define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT
14601#else
14602#include <errno.h> /* For EPIPE, etc. */
14603typedef unsigned long ma_snd_pcm_uframes_t;
14604typedef long ma_snd_pcm_sframes_t;
14605typedef int ma_snd_pcm_stream_t;
14606typedef int ma_snd_pcm_format_t;
14607typedef int ma_snd_pcm_access_t;
14608typedef struct ma_snd_pcm_t ma_snd_pcm_t;
14609typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
14610typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
14611typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
14612typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t;
14613typedef struct
14614{
14615 void* addr;
14616 unsigned int first;
14617 unsigned int step;
14618} ma_snd_pcm_channel_area_t;
14619typedef struct
14620{
14621 unsigned int channels;
14622 unsigned int pos[1];
14623} ma_snd_pcm_chmap_t;
14624
14625/* snd_pcm_state_t */
14626#define MA_SND_PCM_STATE_OPEN 0
14627#define MA_SND_PCM_STATE_SETUP 1
14628#define MA_SND_PCM_STATE_PREPARED 2
14629#define MA_SND_PCM_STATE_RUNNING 3
14630#define MA_SND_PCM_STATE_XRUN 4
14631#define MA_SND_PCM_STATE_DRAINING 5
14632#define MA_SND_PCM_STATE_PAUSED 6
14633#define MA_SND_PCM_STATE_SUSPENDED 7
14634#define MA_SND_PCM_STATE_DISCONNECTED 8
14635
14636/* snd_pcm_stream_t */
14637#define MA_SND_PCM_STREAM_PLAYBACK 0
14638#define MA_SND_PCM_STREAM_CAPTURE 1
14639
14640/* snd_pcm_format_t */
14641#define MA_SND_PCM_FORMAT_UNKNOWN -1
14642#define MA_SND_PCM_FORMAT_U8 1
14643#define MA_SND_PCM_FORMAT_S16_LE 2
14644#define MA_SND_PCM_FORMAT_S16_BE 3
14645#define MA_SND_PCM_FORMAT_S24_LE 6
14646#define MA_SND_PCM_FORMAT_S24_BE 7
14647#define MA_SND_PCM_FORMAT_S32_LE 10
14648#define MA_SND_PCM_FORMAT_S32_BE 11
14649#define MA_SND_PCM_FORMAT_FLOAT_LE 14
14650#define MA_SND_PCM_FORMAT_FLOAT_BE 15
14651#define MA_SND_PCM_FORMAT_FLOAT64_LE 16
14652#define MA_SND_PCM_FORMAT_FLOAT64_BE 17
14653#define MA_SND_PCM_FORMAT_MU_LAW 20
14654#define MA_SND_PCM_FORMAT_A_LAW 21
14655#define MA_SND_PCM_FORMAT_S24_3LE 32
14656#define MA_SND_PCM_FORMAT_S24_3BE 33
14657
14658/* snd_pcm_access_t */
14659#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0
14660#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1
14661#define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2
14662#define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3
14663#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4
14664
14665/* Channel positions. */
14666#define MA_SND_CHMAP_UNKNOWN 0
14667#define MA_SND_CHMAP_NA 1
14668#define MA_SND_CHMAP_MONO 2
14669#define MA_SND_CHMAP_FL 3
14670#define MA_SND_CHMAP_FR 4
14671#define MA_SND_CHMAP_RL 5
14672#define MA_SND_CHMAP_RR 6
14673#define MA_SND_CHMAP_FC 7
14674#define MA_SND_CHMAP_LFE 8
14675#define MA_SND_CHMAP_SL 9
14676#define MA_SND_CHMAP_SR 10
14677#define MA_SND_CHMAP_RC 11
14678#define MA_SND_CHMAP_FLC 12
14679#define MA_SND_CHMAP_FRC 13
14680#define MA_SND_CHMAP_RLC 14
14681#define MA_SND_CHMAP_RRC 15
14682#define MA_SND_CHMAP_FLW 16
14683#define MA_SND_CHMAP_FRW 17
14684#define MA_SND_CHMAP_FLH 18
14685#define MA_SND_CHMAP_FCH 19
14686#define MA_SND_CHMAP_FRH 20
14687#define MA_SND_CHMAP_TC 21
14688#define MA_SND_CHMAP_TFL 22
14689#define MA_SND_CHMAP_TFR 23
14690#define MA_SND_CHMAP_TFC 24
14691#define MA_SND_CHMAP_TRL 25
14692#define MA_SND_CHMAP_TRR 26
14693#define MA_SND_CHMAP_TRC 27
14694#define MA_SND_CHMAP_TFLC 28
14695#define MA_SND_CHMAP_TFRC 29
14696#define MA_SND_CHMAP_TSL 30
14697#define MA_SND_CHMAP_TSR 31
14698#define MA_SND_CHMAP_LLFE 32
14699#define MA_SND_CHMAP_RLFE 33
14700#define MA_SND_CHMAP_BC 34
14701#define MA_SND_CHMAP_BLC 35
14702#define MA_SND_CHMAP_BRC 36
14703
14704/* Open mode flags. */
14705#define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000
14706#define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000
14707#define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000
14708#endif
14709
14710typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode);
14711typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm);
14712typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void);
14713typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
14714typedef 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);
14715typedef 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);
14716typedef void (* ma_snd_pcm_hw_params_get_format_mask_proc) (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask);
14717typedef 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);
14718typedef 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);
14719typedef 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);
14720typedef 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);
14721typedef 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);
14722typedef 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);
14723typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format);
14724typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
14725typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
14726typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
14727typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
14728typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
14729typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
14730typedef int (* ma_snd_pcm_hw_params_get_buffer_size_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val);
14731typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
14732typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access);
14733typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
14734typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void);
14735typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
14736typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val);
14737typedef 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);
14738typedef 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);
14739typedef 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);
14740typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
14741typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void);
14742typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val);
14743typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm);
14744typedef int (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm);
14745typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm);
14746typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm);
14747typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm);
14748typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm);
14749typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints);
14750typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id);
14751typedef int (* ma_snd_card_get_index_proc) (const char *name);
14752typedef int (* ma_snd_device_name_free_hint_proc) (void **hints);
14753typedef 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);
14754typedef 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);
14755typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent);
14756typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size);
14757typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size);
14758typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm);
14759typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm);
14760typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout);
14761typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info);
14762typedef size_t (* ma_snd_pcm_info_sizeof_proc) ();
14763typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info);
14764typedef int (* ma_snd_config_update_free_global_proc) ();
14765
14766/* This array specifies each of the common devices that can be used for both playback and capture. */
14767static const char* g_maCommonDeviceNamesALSA[] = {
14768 "default",
14769 "null",
14770 "pulse",
14771 "jack"
14772};
14773
14774/* This array allows us to blacklist specific playback devices. */
14775static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = {
14776 ""
14777};
14778
14779/* This array allows us to blacklist specific capture devices. */
14780static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = {
14781 ""
14782};
14783
14784
14785/*
14786This array allows miniaudio to control device-specific default buffer sizes. This uses a scaling factor. Order is important. If
14787any part of the string is present in the device's name, the associated scale will be used.
14788*/
14789static struct
14790{
14791 const char* name;
14792 float scale;
14793} g_maDefaultBufferSizeScalesALSA[] = {
14794 {"bcm2835 IEC958/HDMI", 2.0f},
14795 {"bcm2835 ALSA", 2.0f}
14796};
14797
14798static float ma_find_default_buffer_size_scale__alsa(const char* deviceName)
14799{
14800 size_t i;
14801
14802 if (deviceName == NULL) {
14803 return 1;
14804 }
14805
14806 for (i = 0; i < ma_countof(g_maDefaultBufferSizeScalesALSA); ++i) {
14807 if (strstr(g_maDefaultBufferSizeScalesALSA[i].name, deviceName) != NULL) {
14808 return g_maDefaultBufferSizeScalesALSA[i].scale;
14809 }
14810 }
14811
14812 return 1;
14813}
14814
14815static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format)
14816{
14817 ma_snd_pcm_format_t ALSAFormats[] = {
14818 MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */
14819 MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */
14820 MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */
14821 MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */
14822 MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */
14823 MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */
14824 };
14825
14826 if (ma_is_big_endian()) {
14827 ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN;
14828 ALSAFormats[1] = MA_SND_PCM_FORMAT_U8;
14829 ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE;
14830 ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE;
14831 ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE;
14832 ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE;
14833 }
14834
14835 return ALSAFormats[format];
14836}
14837
14838static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA)
14839{
14840 if (ma_is_little_endian()) {
14841 switch (formatALSA) {
14842 case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16;
14843 case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24;
14844 case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32;
14845 case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32;
14846 default: break;
14847 }
14848 } else {
14849 switch (formatALSA) {
14850 case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16;
14851 case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24;
14852 case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32;
14853 case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32;
14854 default: break;
14855 }
14856 }
14857
14858 /* Endian agnostic. */
14859 switch (formatALSA) {
14860 case MA_SND_PCM_FORMAT_U8: return ma_format_u8;
14861 default: return ma_format_unknown;
14862 }
14863}
14864
14865static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos)
14866{
14867 switch (alsaChannelPos)
14868 {
14869 case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO;
14870 case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT;
14871 case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT;
14872 case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT;
14873 case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT;
14874 case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER;
14875 case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE;
14876 case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT;
14877 case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT;
14878 case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER;
14879 case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER;
14880 case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER;
14881 case MA_SND_CHMAP_RLC: return 0;
14882 case MA_SND_CHMAP_RRC: return 0;
14883 case MA_SND_CHMAP_FLW: return 0;
14884 case MA_SND_CHMAP_FRW: return 0;
14885 case MA_SND_CHMAP_FLH: return 0;
14886 case MA_SND_CHMAP_FCH: return 0;
14887 case MA_SND_CHMAP_FRH: return 0;
14888 case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER;
14889 case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT;
14890 case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT;
14891 case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER;
14892 case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT;
14893 case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT;
14894 case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER;
14895 default: break;
14896 }
14897
14898 return 0;
14899}
14900
14901static ma_bool32 ma_is_common_device_name__alsa(const char* name)
14902{
14903 size_t iName;
14904 for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) {
14905 if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) {
14906 return MA_TRUE;
14907 }
14908 }
14909
14910 return MA_FALSE;
14911}
14912
14913
14914static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name)
14915{
14916 size_t iName;
14917 for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) {
14918 if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) {
14919 return MA_TRUE;
14920 }
14921 }
14922
14923 return MA_FALSE;
14924}
14925
14926static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name)
14927{
14928 size_t iName;
14929 for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) {
14930 if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) {
14931 return MA_TRUE;
14932 }
14933 }
14934
14935 return MA_FALSE;
14936}
14937
14938static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name)
14939{
14940 if (deviceType == ma_device_type_playback) {
14941 return ma_is_playback_device_blacklisted__alsa(name);
14942 } else {
14943 return ma_is_capture_device_blacklisted__alsa(name);
14944 }
14945}
14946
14947
14948static const char* ma_find_char(const char* str, char c, int* index)
14949{
14950 int i = 0;
14951 for (;;) {
14952 if (str[i] == '\0') {
14953 if (index) *index = -1;
14954 return NULL;
14955 }
14956
14957 if (str[i] == c) {
14958 if (index) *index = i;
14959 return str + i;
14960 }
14961
14962 i += 1;
14963 }
14964
14965 /* Should never get here, but treat it as though the character was not found to make me feel better inside. */
14966 if (index) *index = -1;
14967 return NULL;
14968}
14969
14970static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid)
14971{
14972 /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */
14973
14974 int commaPos;
14975 const char* dev;
14976 int i;
14977
14978 if (hwid == NULL) {
14979 return MA_FALSE;
14980 }
14981
14982 if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') {
14983 return MA_FALSE;
14984 }
14985
14986 hwid += 3;
14987
14988 dev = ma_find_char(hwid, ',', &commaPos);
14989 if (dev == NULL) {
14990 return MA_FALSE;
14991 } else {
14992 dev += 1; /* Skip past the ",". */
14993 }
14994
14995 /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */
14996 for (i = 0; i < commaPos; ++i) {
14997 if (hwid[i] < '0' || hwid[i] > '9') {
14998 return MA_FALSE;
14999 }
15000 }
15001
15002 /* Check if everything after the "," is numeric. If not, return false. */
15003 i = 0;
15004 while (dev[i] != '\0') {
15005 if (dev[i] < '0' || dev[i] > '9') {
15006 return MA_FALSE;
15007 }
15008 i += 1;
15009 }
15010
15011 return MA_TRUE;
15012}
15013
15014static 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. */
15015{
15016 /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */
15017
15018 int colonPos;
15019 int commaPos;
15020 char card[256];
15021 const char* dev;
15022 int cardIndex;
15023
15024 if (dst == NULL) {
15025 return -1;
15026 }
15027 if (dstSize < 7) {
15028 return -1; /* Absolute minimum size of the output buffer is 7 bytes. */
15029 }
15030
15031 *dst = '\0'; /* Safety. */
15032 if (src == NULL) {
15033 return -1;
15034 }
15035
15036 /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */
15037 if (ma_is_device_name_in_hw_format__alsa(src)) {
15038 return ma_strcpy_s(dst, dstSize, src);
15039 }
15040
15041 src = ma_find_char(src, ':', &colonPos);
15042 if (src == NULL) {
15043 return -1; /* Couldn't find a colon */
15044 }
15045
15046 dev = ma_find_char(src, ',', &commaPos);
15047 if (dev == NULL) {
15048 dev = "0";
15049 ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */
15050 } else {
15051 dev = dev + 5; /* +5 = ",DEV=" */
15052 ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */
15053 }
15054
15055 cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card);
15056 if (cardIndex < 0) {
15057 return -2; /* Failed to retrieve the card index. */
15058 }
15059
15060 /*printf("TESTING: CARD=%s,DEV=%s\n", card, dev); */
15061
15062
15063 /* Construction. */
15064 dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':';
15065 if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) {
15066 return -3;
15067 }
15068 if (ma_strcat_s(dst, dstSize, ",") != 0) {
15069 return -3;
15070 }
15071 if (ma_strcat_s(dst, dstSize, dev) != 0) {
15072 return -3;
15073 }
15074
15075 return 0;
15076}
15077
15078static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID)
15079{
15080 ma_uint32 i;
15081
15082 MA_ASSERT(pHWID != NULL);
15083
15084 for (i = 0; i < count; ++i) {
15085 if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) {
15086 return MA_TRUE;
15087 }
15088 }
15089
15090 return MA_FALSE;
15091}
15092
15093
15094static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_snd_pcm_t** ppPCM)
15095{
15096 ma_snd_pcm_t* pPCM;
15097 ma_snd_pcm_stream_t stream;
15098 int openMode;
15099
15100 MA_ASSERT(pContext != NULL);
15101 MA_ASSERT(ppPCM != NULL);
15102
15103 *ppPCM = NULL;
15104 pPCM = NULL;
15105
15106 stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE;
15107 openMode = MA_SND_PCM_NO_AUTO_RESAMPLE | MA_SND_PCM_NO_AUTO_CHANNELS | MA_SND_PCM_NO_AUTO_FORMAT;
15108
15109 if (pDeviceID == NULL) {
15110 ma_bool32 isDeviceOpen;
15111 size_t i;
15112
15113 /*
15114 We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes
15115 me feel better to try as hard as we can get to get _something_ working.
15116 */
15117 const char* defaultDeviceNames[] = {
15118 "default",
15119 NULL,
15120 NULL,
15121 NULL,
15122 NULL,
15123 NULL,
15124 NULL
15125 };
15126
15127 if (shareMode == ma_share_mode_exclusive) {
15128 defaultDeviceNames[1] = "hw";
15129 defaultDeviceNames[2] = "hw:0";
15130 defaultDeviceNames[3] = "hw:0,0";
15131 } else {
15132 if (deviceType == ma_device_type_playback) {
15133 defaultDeviceNames[1] = "dmix";
15134 defaultDeviceNames[2] = "dmix:0";
15135 defaultDeviceNames[3] = "dmix:0,0";
15136 } else {
15137 defaultDeviceNames[1] = "dsnoop";
15138 defaultDeviceNames[2] = "dsnoop:0";
15139 defaultDeviceNames[3] = "dsnoop:0,0";
15140 }
15141 defaultDeviceNames[4] = "hw";
15142 defaultDeviceNames[5] = "hw:0";
15143 defaultDeviceNames[6] = "hw:0,0";
15144 }
15145
15146 isDeviceOpen = MA_FALSE;
15147 for (i = 0; i < ma_countof(defaultDeviceNames); ++i) {
15148 if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') {
15149 if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) {
15150 isDeviceOpen = MA_TRUE;
15151 break;
15152 }
15153 }
15154 }
15155
15156 if (!isDeviceOpen) {
15157 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);
15158 }
15159 } else {
15160 /*
15161 We're trying to open a specific device. There's a few things to consider here:
15162
15163 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
15164 an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it
15165 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").
15166 */
15167
15168 /* May end up needing to make small adjustments to the ID, so make a copy. */
15169 ma_device_id deviceID = *pDeviceID;
15170 ma_bool32 isDeviceOpen = MA_FALSE;
15171
15172 if (deviceID.alsa[0] != ':') {
15173 /* The ID is not in ":0,0" format. Use the ID exactly as-is. */
15174 if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode) == 0) {
15175 isDeviceOpen = MA_TRUE;
15176 }
15177 } else {
15178 char hwid[256];
15179
15180 /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */
15181 if (deviceID.alsa[1] == '\0') {
15182 deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */
15183 }
15184
15185 if (shareMode == ma_share_mode_shared) {
15186 if (deviceType == ma_device_type_playback) {
15187 ma_strcpy_s(hwid, sizeof(hwid), "dmix");
15188 } else {
15189 ma_strcpy_s(hwid, sizeof(hwid), "dsnoop");
15190 }
15191
15192 if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
15193 if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode) == 0) {
15194 isDeviceOpen = MA_TRUE;
15195 }
15196 }
15197 }
15198
15199 /* 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. */
15200 if (!isDeviceOpen) {
15201 ma_strcpy_s(hwid, sizeof(hwid), "hw");
15202 if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
15203 if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode) == 0) {
15204 isDeviceOpen = MA_TRUE;
15205 }
15206 }
15207 }
15208 }
15209
15210 if (!isDeviceOpen) {
15211 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
15212 }
15213 }
15214
15215 *ppPCM = pPCM;
15216 return MA_SUCCESS;
15217}
15218
15219
15220static ma_bool32 ma_context_is_device_id_equal__alsa(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
15221{
15222 MA_ASSERT(pContext != NULL);
15223 MA_ASSERT(pID0 != NULL);
15224 MA_ASSERT(pID1 != NULL);
15225 (void)pContext;
15226
15227 return ma_strcmp(pID0->alsa, pID1->alsa) == 0;
15228}
15229
15230static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
15231{
15232 ma_bool32 cbResult = MA_TRUE;
15233 char** ppDeviceHints;
15234 ma_device_id* pUniqueIDs = NULL;
15235 ma_uint32 uniqueIDCount = 0;
15236 char** ppNextDeviceHint;
15237
15238 MA_ASSERT(pContext != NULL);
15239 MA_ASSERT(callback != NULL);
15240
15241 ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock);
15242
15243 if (((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints) < 0) {
15244 ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
15245 return MA_NO_BACKEND;
15246 }
15247
15248 ppNextDeviceHint = ppDeviceHints;
15249 while (*ppNextDeviceHint != NULL) {
15250 char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME");
15251 char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC");
15252 char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID");
15253 ma_device_type deviceType = ma_device_type_playback;
15254 ma_bool32 stopEnumeration = MA_FALSE;
15255 char hwid[sizeof(pUniqueIDs->alsa)];
15256 ma_device_info deviceInfo;
15257
15258 if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) {
15259 deviceType = ma_device_type_playback;
15260 }
15261 if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) {
15262 deviceType = ma_device_type_capture;
15263 }
15264
15265 if (NAME != NULL) {
15266 if (pContext->alsa.useVerboseDeviceEnumeration) {
15267 /* Verbose mode. Use the name exactly as-is. */
15268 ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
15269 } else {
15270 /* Simplified mode. Use ":%d,%d" format. */
15271 if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) {
15272 /*
15273 At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the
15274 plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device
15275 initialization time and is used as an indicator to try and use the most appropriate plugin depending on the
15276 device type and sharing mode.
15277 */
15278 char* dst = hwid;
15279 char* src = hwid+2;
15280 while ((*dst++ = *src++));
15281 } else {
15282 /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */
15283 ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
15284 }
15285
15286 if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) {
15287 goto next_device; /* The device has already been enumerated. Move on to the next one. */
15288 } else {
15289 /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */
15290 size_t oldCapacity = sizeof(*pUniqueIDs) * uniqueIDCount;
15291 size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1);
15292 ma_device_id* pNewUniqueIDs = (ma_device_id*)ma__realloc_from_callbacks(pUniqueIDs, newCapacity, oldCapacity, &pContext->allocationCallbacks);
15293 if (pNewUniqueIDs == NULL) {
15294 goto next_device; /* Failed to allocate memory. */
15295 }
15296
15297 pUniqueIDs = pNewUniqueIDs;
15298 MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid));
15299 uniqueIDCount += 1;
15300 }
15301 }
15302 } else {
15303 MA_ZERO_MEMORY(hwid, sizeof(hwid));
15304 }
15305
15306 MA_ZERO_OBJECT(&deviceInfo);
15307 ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1);
15308
15309 /*
15310 DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose
15311 device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish
15312 between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the
15313 description.
15314
15315 The value in DESC seems to be split into two lines, with the first line being the name of the device and the
15316 second line being a description of the device. I don't like having the description be across two lines because
15317 it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line
15318 being put into parentheses. In simplified mode I'm just stripping the second line entirely.
15319 */
15320 if (DESC != NULL) {
15321 int lfPos;
15322 const char* line2 = ma_find_char(DESC, '\n', &lfPos);
15323 if (line2 != NULL) {
15324 line2 += 1; /* Skip past the new-line character. */
15325
15326 if (pContext->alsa.useVerboseDeviceEnumeration) {
15327 /* Verbose mode. Put the second line in brackets. */
15328 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
15329 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " (");
15330 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2);
15331 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")");
15332 } else {
15333 /* Simplified mode. Strip the second line entirely. */
15334 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
15335 }
15336 } else {
15337 /* There's no second line. Just copy the whole description. */
15338 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1);
15339 }
15340 }
15341
15342 if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) {
15343 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
15344 }
15345
15346 /*
15347 Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback
15348 again for the other device type in this case. We do this for known devices.
15349 */
15350 if (cbResult) {
15351 if (ma_is_common_device_name__alsa(NAME)) {
15352 if (deviceType == ma_device_type_playback) {
15353 if (!ma_is_capture_device_blacklisted__alsa(NAME)) {
15354 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
15355 }
15356 } else {
15357 if (!ma_is_playback_device_blacklisted__alsa(NAME)) {
15358 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
15359 }
15360 }
15361 }
15362 }
15363
15364 if (cbResult == MA_FALSE) {
15365 stopEnumeration = MA_TRUE;
15366 }
15367
15368 next_device:
15369 free(NAME);
15370 free(DESC);
15371 free(IOID);
15372 ppNextDeviceHint += 1;
15373
15374 /* We need to stop enumeration if the callback returned false. */
15375 if (stopEnumeration) {
15376 break;
15377 }
15378 }
15379
15380 ma__free_from_callbacks(pUniqueIDs, &pContext->allocationCallbacks);
15381 ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints);
15382
15383 ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
15384
15385 return MA_SUCCESS;
15386}
15387
15388
15389typedef struct
15390{
15391 ma_device_type deviceType;
15392 const ma_device_id* pDeviceID;
15393 ma_share_mode shareMode;
15394 ma_device_info* pDeviceInfo;
15395 ma_bool32 foundDevice;
15396} ma_context_get_device_info_enum_callback_data__alsa;
15397
15398static ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData)
15399{
15400 ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData;
15401 MA_ASSERT(pData != NULL);
15402
15403 if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
15404 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
15405 pData->foundDevice = MA_TRUE;
15406 } else {
15407 if (pData->deviceType == deviceType && ma_context_is_device_id_equal__alsa(pContext, pData->pDeviceID, &pDeviceInfo->id)) {
15408 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
15409 pData->foundDevice = MA_TRUE;
15410 }
15411 }
15412
15413 /* Keep enumerating until we have found the device. */
15414 return !pData->foundDevice;
15415}
15416
15417static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
15418{
15419 ma_context_get_device_info_enum_callback_data__alsa data;
15420 ma_result result;
15421 ma_snd_pcm_t* pPCM;
15422 ma_snd_pcm_hw_params_t* pHWParams;
15423 ma_snd_pcm_format_mask_t* pFormatMask;
15424 int sampleRateDir = 0;
15425
15426 MA_ASSERT(pContext != NULL);
15427
15428 /* We just enumerate to find basic information about the device. */
15429 data.deviceType = deviceType;
15430 data.pDeviceID = pDeviceID;
15431 data.shareMode = shareMode;
15432 data.pDeviceInfo = pDeviceInfo;
15433 data.foundDevice = MA_FALSE;
15434 result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data);
15435 if (result != MA_SUCCESS) {
15436 return result;
15437 }
15438
15439 if (!data.foundDevice) {
15440 return MA_NO_DEVICE;
15441 }
15442
15443 /* For detailed info we need to open the device. */
15444 result = ma_context_open_pcm__alsa(pContext, shareMode, deviceType, pDeviceID, &pPCM);
15445 if (result != MA_SUCCESS) {
15446 return result;
15447 }
15448
15449 /* We need to initialize a HW parameters object in order to know what formats are supported. */
15450 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);
15451 if (pHWParams == NULL) {
15452 return MA_OUT_OF_MEMORY;
15453 }
15454
15455 if (((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams) < 0) {
15456 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
15457 }
15458
15459 ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &pDeviceInfo->minChannels);
15460 ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &pDeviceInfo->maxChannels);
15461 ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &pDeviceInfo->minSampleRate, &sampleRateDir);
15462 ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &pDeviceInfo->maxSampleRate, &sampleRateDir);
15463
15464 /* Formats. */
15465 pFormatMask = (ma_snd_pcm_format_mask_t*)ma__calloc_from_callbacks(((ma_snd_pcm_format_mask_sizeof_proc)pContext->alsa.snd_pcm_format_mask_sizeof)(), &pContext->allocationCallbacks);
15466 if (pFormatMask == NULL) {
15467 return MA_OUT_OF_MEMORY;
15468 }
15469
15470 ((ma_snd_pcm_hw_params_get_format_mask_proc)pContext->alsa.snd_pcm_hw_params_get_format_mask)(pHWParams, pFormatMask);
15471
15472 pDeviceInfo->formatCount = 0;
15473 if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_U8)) {
15474 pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_u8;
15475 }
15476 if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_S16_LE)) {
15477 pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s16;
15478 }
15479 if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_S24_3LE)) {
15480 pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s24;
15481 }
15482 if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_S32_LE)) {
15483 pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s32;
15484 }
15485 if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_FLOAT_LE)) {
15486 pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_f32;
15487 }
15488
15489 ma__free_from_callbacks(pFormatMask, &pContext->allocationCallbacks);
15490 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
15491
15492 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
15493 return MA_SUCCESS;
15494}
15495
15496
15497#if 0
15498/*
15499Waits for a number of frames to become available for either capture or playback. The return
15500value is the number of frames available.
15501
15502This will return early if the main loop is broken with ma_device__break_main_loop().
15503*/
15504static ma_uint32 ma_device__wait_for_frames__alsa(ma_device* pDevice, ma_bool32* pRequiresRestart)
15505{
15506 MA_ASSERT(pDevice != NULL);
15507
15508 if (pRequiresRestart) *pRequiresRestart = MA_FALSE;
15509
15510 /* I want it so that this function returns the period size in frames. We just wait until that number of frames are available and then return. */
15511 ma_uint32 periodSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods;
15512 while (!pDevice->alsa.breakFromMainLoop) {
15513 ma_snd_pcm_sframes_t framesAvailable = ((ma_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((ma_snd_pcm_t*)pDevice->alsa.pPCM);
15514 if (framesAvailable < 0) {
15515 if (framesAvailable == -EPIPE) {
15516 if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, framesAvailable, MA_TRUE) < 0) {
15517 return 0;
15518 }
15519
15520 /* A device recovery means a restart for mmap mode. */
15521 if (pRequiresRestart) {
15522 *pRequiresRestart = MA_TRUE;
15523 }
15524
15525 /* Try again, but if it fails this time just return an error. */
15526 framesAvailable = ((ma_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((ma_snd_pcm_t*)pDevice->alsa.pPCM);
15527 if (framesAvailable < 0) {
15528 return 0;
15529 }
15530 }
15531 }
15532
15533 if (framesAvailable >= periodSizeInFrames) {
15534 return periodSizeInFrames;
15535 }
15536
15537 if (framesAvailable < periodSizeInFrames) {
15538 /* Less than a whole period is available so keep waiting. */
15539 int waitResult = ((ma_snd_pcm_wait_proc)pDevice->pContext->alsa.snd_pcm_wait)((ma_snd_pcm_t*)pDevice->alsa.pPCM, -1);
15540 if (waitResult < 0) {
15541 if (waitResult == -EPIPE) {
15542 if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, waitResult, MA_TRUE) < 0) {
15543 return 0;
15544 }
15545
15546 /* A device recovery means a restart for mmap mode. */
15547 if (pRequiresRestart) {
15548 *pRequiresRestart = MA_TRUE;
15549 }
15550 }
15551 }
15552 }
15553 }
15554
15555 /* We'll get here if the loop was terminated. Just return whatever's available. */
15556 ma_snd_pcm_sframes_t framesAvailable = ((ma_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((ma_snd_pcm_t*)pDevice->alsa.pPCM);
15557 if (framesAvailable < 0) {
15558 return 0;
15559 }
15560
15561 return framesAvailable;
15562}
15563
15564static ma_bool32 ma_device_read_from_client_and_write__alsa(ma_device* pDevice)
15565{
15566 MA_ASSERT(pDevice != NULL);
15567 if (!ma_device_is_started(pDevice) && ma_device__get_state(pDevice) != MA_STATE_STARTING) {
15568 return MA_FALSE;
15569 }
15570 if (pDevice->alsa.breakFromMainLoop) {
15571 return MA_FALSE;
15572 }
15573
15574 if (pDevice->alsa.isUsingMMap) {
15575 /* mmap. */
15576 ma_bool32 requiresRestart;
15577 ma_uint32 framesAvailable = ma_device__wait_for_frames__alsa(pDevice, &requiresRestart);
15578 if (framesAvailable == 0) {
15579 return MA_FALSE;
15580 }
15581
15582 /* Don't bother asking the client for more audio data if we're just stopping the device anyway. */
15583 if (pDevice->alsa.breakFromMainLoop) {
15584 return MA_FALSE;
15585 }
15586
15587 const ma_snd_pcm_channel_area_t* pAreas;
15588 ma_snd_pcm_uframes_t mappedOffset;
15589 ma_snd_pcm_uframes_t mappedFrames = framesAvailable;
15590 while (framesAvailable > 0) {
15591 int result = ((ma_snd_pcm_mmap_begin_proc)pDevice->pContext->alsa.snd_pcm_mmap_begin)((ma_snd_pcm_t*)pDevice->alsa.pPCM, &pAreas, &mappedOffset, &mappedFrames);
15592 if (result < 0) {
15593 return MA_FALSE;
15594 }
15595
15596 if (mappedFrames > 0) {
15597 void* pBuffer = (ma_uint8*)pAreas[0].addr + ((pAreas[0].first + (mappedOffset * pAreas[0].step)) / 8);
15598 ma_device__read_frames_from_client(pDevice, mappedFrames, pBuffer);
15599 }
15600
15601 result = ((ma_snd_pcm_mmap_commit_proc)pDevice->pContext->alsa.snd_pcm_mmap_commit)((ma_snd_pcm_t*)pDevice->alsa.pPCM, mappedOffset, mappedFrames);
15602 if (result < 0 || (ma_snd_pcm_uframes_t)result != mappedFrames) {
15603 ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, result, MA_TRUE);
15604 return MA_FALSE;
15605 }
15606
15607 if (requiresRestart) {
15608 if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCM) < 0) {
15609 return MA_FALSE;
15610 }
15611 }
15612
15613 if (framesAvailable >= mappedFrames) {
15614 framesAvailable -= mappedFrames;
15615 } else {
15616 framesAvailable = 0;
15617 }
15618 }
15619 } else {
15620 /* readi/writei. */
15621 while (!pDevice->alsa.breakFromMainLoop) {
15622 ma_uint32 framesAvailable = ma_device__wait_for_frames__alsa(pDevice, NULL);
15623 if (framesAvailable == 0) {
15624 continue;
15625 }
15626
15627 /* Don't bother asking the client for more audio data if we're just stopping the device anyway. */
15628 if (pDevice->alsa.breakFromMainLoop) {
15629 return MA_FALSE;
15630 }
15631
15632 ma_device__read_frames_from_client(pDevice, framesAvailable, pDevice->alsa.pIntermediaryBuffer);
15633
15634 ma_snd_pcm_sframes_t framesWritten = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
15635 if (framesWritten < 0) {
15636 if (framesWritten == -EAGAIN) {
15637 continue; /* Just keep trying... */
15638 } else if (framesWritten == -EPIPE) {
15639 /* Underrun. Just recover and try writing again. */
15640 if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, framesWritten, MA_TRUE) < 0) {
15641 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
15642 return MA_FALSE;
15643 }
15644
15645 framesWritten = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
15646 if (framesWritten < 0) {
15647 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to write data to the internal device.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
15648 return MA_FALSE;
15649 }
15650
15651 break; /* Success. */
15652 } else {
15653 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_writei() failed when writing initial data.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
15654 return MA_FALSE;
15655 }
15656 } else {
15657 break; /* Success. */
15658 }
15659 }
15660 }
15661
15662 return MA_TRUE;
15663}
15664
15665static ma_bool32 ma_device_read_and_send_to_client__alsa(ma_device* pDevice)
15666{
15667 MA_ASSERT(pDevice != NULL);
15668 if (!ma_device_is_started(pDevice)) {
15669 return MA_FALSE;
15670 }
15671 if (pDevice->alsa.breakFromMainLoop) {
15672 return MA_FALSE;
15673 }
15674
15675 ma_uint32 framesToSend = 0;
15676 void* pBuffer = NULL;
15677 if (pDevice->alsa.pIntermediaryBuffer == NULL) {
15678 /* mmap. */
15679 ma_bool32 requiresRestart;
15680 ma_uint32 framesAvailable = ma_device__wait_for_frames__alsa(pDevice, &requiresRestart);
15681 if (framesAvailable == 0) {
15682 return MA_FALSE;
15683 }
15684
15685 const ma_snd_pcm_channel_area_t* pAreas;
15686 ma_snd_pcm_uframes_t mappedOffset;
15687 ma_snd_pcm_uframes_t mappedFrames = framesAvailable;
15688 while (framesAvailable > 0) {
15689 int result = ((ma_snd_pcm_mmap_begin_proc)pDevice->pContext->alsa.snd_pcm_mmap_begin)((ma_snd_pcm_t*)pDevice->alsa.pPCM, &pAreas, &mappedOffset, &mappedFrames);
15690 if (result < 0) {
15691 return MA_FALSE;
15692 }
15693
15694 if (mappedFrames > 0) {
15695 void* pBuffer = (ma_uint8*)pAreas[0].addr + ((pAreas[0].first + (mappedOffset * pAreas[0].step)) / 8);
15696 ma_device__send_frames_to_client(pDevice, mappedFrames, pBuffer);
15697 }
15698
15699 result = ((ma_snd_pcm_mmap_commit_proc)pDevice->pContext->alsa.snd_pcm_mmap_commit)((ma_snd_pcm_t*)pDevice->alsa.pPCM, mappedOffset, mappedFrames);
15700 if (result < 0 || (ma_snd_pcm_uframes_t)result != mappedFrames) {
15701 ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, result, MA_TRUE);
15702 return MA_FALSE;
15703 }
15704
15705 if (requiresRestart) {
15706 if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCM) < 0) {
15707 return MA_FALSE;
15708 }
15709 }
15710
15711 if (framesAvailable >= mappedFrames) {
15712 framesAvailable -= mappedFrames;
15713 } else {
15714 framesAvailable = 0;
15715 }
15716 }
15717 } else {
15718 /* readi/writei. */
15719 ma_snd_pcm_sframes_t framesRead = 0;
15720 while (!pDevice->alsa.breakFromMainLoop) {
15721 ma_uint32 framesAvailable = ma_device__wait_for_frames__alsa(pDevice, NULL);
15722 if (framesAvailable == 0) {
15723 continue;
15724 }
15725
15726 framesRead = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
15727 if (framesRead < 0) {
15728 if (framesRead == -EAGAIN) {
15729 continue; /* Just keep trying... */
15730 } else if (framesRead == -EPIPE) {
15731 /* Overrun. Just recover and try reading again. */
15732 if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, framesRead, MA_TRUE) < 0) {
15733 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
15734 return MA_FALSE;
15735 }
15736
15737 framesRead = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
15738 if (framesRead < 0) {
15739 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to read data from the internal device.", MA_FAILED_TO_READ_DATA_FROM_DEVICE);
15740 return MA_FALSE;
15741 }
15742
15743 break; /* Success. */
15744 } else {
15745 return MA_FALSE;
15746 }
15747 } else {
15748 break; /* Success. */
15749 }
15750 }
15751
15752 framesToSend = framesRead;
15753 pBuffer = pDevice->alsa.pIntermediaryBuffer;
15754 }
15755
15756 if (framesToSend > 0) {
15757 ma_device__send_frames_to_client(pDevice, framesToSend, pBuffer);
15758 }
15759
15760 return MA_TRUE;
15761}
15762#endif /* 0 */
15763
15764static void ma_device_uninit__alsa(ma_device* pDevice)
15765{
15766 MA_ASSERT(pDevice != NULL);
15767
15768 if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) {
15769 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
15770 }
15771
15772 if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) {
15773 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
15774 }
15775}
15776
15777static ma_result ma_device_init_by_type__alsa(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
15778{
15779 ma_result result;
15780 ma_snd_pcm_t* pPCM;
15781 ma_bool32 isUsingMMap;
15782 ma_snd_pcm_format_t formatALSA;
15783 ma_share_mode shareMode;
15784 ma_device_id* pDeviceID;
15785 ma_format internalFormat;
15786 ma_uint32 internalChannels;
15787 ma_uint32 internalSampleRate;
15788 ma_channel internalChannelMap[MA_MAX_CHANNELS];
15789 ma_uint32 internalPeriodSizeInFrames;
15790 ma_uint32 internalPeriods;
15791 ma_snd_pcm_hw_params_t* pHWParams;
15792 ma_snd_pcm_sw_params_t* pSWParams;
15793 ma_snd_pcm_uframes_t bufferBoundary;
15794 float bufferSizeScaleFactor;
15795
15796 MA_ASSERT(pContext != NULL);
15797 MA_ASSERT(pConfig != NULL);
15798 MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */
15799 MA_ASSERT(pDevice != NULL);
15800
15801 formatALSA = ma_convert_ma_format_to_alsa_format((deviceType == ma_device_type_capture) ? pConfig->capture.format : pConfig->playback.format);
15802 shareMode = (deviceType == ma_device_type_capture) ? pConfig->capture.shareMode : pConfig->playback.shareMode;
15803 pDeviceID = (deviceType == ma_device_type_capture) ? pConfig->capture.pDeviceID : pConfig->playback.pDeviceID;
15804
15805 result = ma_context_open_pcm__alsa(pContext, shareMode, deviceType, pDeviceID, &pPCM);
15806 if (result != MA_SUCCESS) {
15807 return result;
15808 }
15809
15810 /* If using the default buffer size we may want to apply some device-specific scaling for known devices that have peculiar latency characteristics */
15811 bufferSizeScaleFactor = 1;
15812 if (pDevice->usingDefaultBufferSize) {
15813 ma_snd_pcm_info_t* pInfo = (ma_snd_pcm_info_t*)ma__calloc_from_callbacks(((ma_snd_pcm_info_sizeof_proc)pContext->alsa.snd_pcm_info_sizeof)(), &pContext->allocationCallbacks);
15814 if (pInfo == NULL) {
15815 return MA_OUT_OF_MEMORY;
15816 }
15817
15818 /* We may need to scale the size of the buffer depending on the device. */
15819 if (((ma_snd_pcm_info_proc)pContext->alsa.snd_pcm_info)(pPCM, pInfo) == 0) {
15820 const char* deviceName = ((ma_snd_pcm_info_get_name_proc)pContext->alsa.snd_pcm_info_get_name)(pInfo);
15821 if (deviceName != NULL) {
15822 if (ma_strcmp(deviceName, "default") == 0) {
15823 char** ppDeviceHints;
15824 char** ppNextDeviceHint;
15825
15826 /* It's the default device. We need to use DESC from snd_device_name_hint(). */
15827 if (((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints) < 0) {
15828 ma__free_from_callbacks(pInfo, &pContext->allocationCallbacks);
15829 return MA_NO_BACKEND;
15830 }
15831
15832 ppNextDeviceHint = ppDeviceHints;
15833 while (*ppNextDeviceHint != NULL) {
15834 char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME");
15835 char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC");
15836 char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID");
15837
15838 ma_bool32 foundDevice = MA_FALSE;
15839 if ((deviceType == ma_device_type_playback && (IOID == NULL || ma_strcmp(IOID, "Output") == 0)) ||
15840 (deviceType == ma_device_type_capture && (IOID != NULL && ma_strcmp(IOID, "Input" ) == 0))) {
15841 if (ma_strcmp(NAME, deviceName) == 0) {
15842 bufferSizeScaleFactor = ma_find_default_buffer_size_scale__alsa(DESC);
15843 foundDevice = MA_TRUE;
15844 }
15845 }
15846
15847 free(NAME);
15848 free(DESC);
15849 free(IOID);
15850 ppNextDeviceHint += 1;
15851
15852 if (foundDevice) {
15853 break;
15854 }
15855 }
15856
15857 ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints);
15858 } else {
15859 bufferSizeScaleFactor = ma_find_default_buffer_size_scale__alsa(deviceName);
15860 }
15861 }
15862 }
15863
15864 ma__free_from_callbacks(pInfo, &pContext->allocationCallbacks);
15865 }
15866
15867
15868 /* Hardware parameters. */
15869 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);
15870 if (pHWParams == NULL) {
15871 return MA_OUT_OF_MEMORY;
15872 }
15873
15874 if (((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams) < 0) {
15875 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
15876 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
15877 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
15878 }
15879
15880 /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */
15881 isUsingMMap = MA_FALSE;
15882#if 0 /* NOTE: MMAP mode temporarily disabled. */
15883 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. */
15884 if (!pConfig->alsa.noMMap && ma_device__is_async(pDevice)) {
15885 if (((ma_snd_pcm_hw_params_set_access_proc)pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) {
15886 pDevice->alsa.isUsingMMap = MA_TRUE;
15887 }
15888 }
15889 }
15890#endif
15891
15892 if (!isUsingMMap) {
15893 if (((ma_snd_pcm_hw_params_set_access_proc)pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
15894 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
15895 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
15896 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_FORMAT_NOT_SUPPORTED);
15897 }
15898 }
15899
15900 /*
15901 Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't
15902 find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS.
15903 */
15904
15905 /* Format. */
15906 {
15907 ma_snd_pcm_format_mask_t* pFormatMask;
15908
15909 /* Try getting every supported format first. */
15910 pFormatMask = (ma_snd_pcm_format_mask_t*)ma__calloc_from_callbacks(((ma_snd_pcm_format_mask_sizeof_proc)pContext->alsa.snd_pcm_format_mask_sizeof)(), &pContext->allocationCallbacks);
15911 if (pFormatMask == NULL) {
15912 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
15913 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
15914 return MA_OUT_OF_MEMORY;
15915 }
15916
15917 ((ma_snd_pcm_hw_params_get_format_mask_proc)pContext->alsa.snd_pcm_hw_params_get_format_mask)(pHWParams, pFormatMask);
15918
15919 /*
15920 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
15921 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.
15922 */
15923 if (!((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, formatALSA)) {
15924 size_t i;
15925
15926 /* The requested format is not supported so now try running through the list of formats and return the best one. */
15927 ma_snd_pcm_format_t preferredFormatsALSA[] = {
15928 MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */
15929 MA_SND_PCM_FORMAT_FLOAT_LE, /* ma_format_f32 */
15930 MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */
15931 MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */
15932 MA_SND_PCM_FORMAT_U8 /* ma_format_u8 */
15933 };
15934
15935 if (ma_is_big_endian()) {
15936 preferredFormatsALSA[0] = MA_SND_PCM_FORMAT_S16_BE;
15937 preferredFormatsALSA[1] = MA_SND_PCM_FORMAT_FLOAT_BE;
15938 preferredFormatsALSA[2] = MA_SND_PCM_FORMAT_S32_BE;
15939 preferredFormatsALSA[3] = MA_SND_PCM_FORMAT_S24_3BE;
15940 preferredFormatsALSA[4] = MA_SND_PCM_FORMAT_U8;
15941 }
15942
15943 formatALSA = MA_SND_PCM_FORMAT_UNKNOWN;
15944 for (i = 0; i < (sizeof(preferredFormatsALSA) / sizeof(preferredFormatsALSA[0])); ++i) {
15945 if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, preferredFormatsALSA[i])) {
15946 formatALSA = preferredFormatsALSA[i];
15947 break;
15948 }
15949 }
15950
15951 if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) {
15952 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
15953 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
15954 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);
15955 }
15956 }
15957
15958 ma__free_from_callbacks(pFormatMask, &pContext->allocationCallbacks);
15959 pFormatMask = NULL;
15960
15961 if (((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA) < 0) {
15962 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
15963 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
15964 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.", MA_FORMAT_NOT_SUPPORTED);
15965 }
15966
15967 internalFormat = ma_format_from_alsa(formatALSA);
15968 if (internalFormat == ma_format_unknown) {
15969 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
15970 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
15971 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
15972 }
15973 }
15974
15975 /* Channels. */
15976 {
15977 unsigned int channels = (deviceType == ma_device_type_capture) ? pConfig->capture.channels : pConfig->playback.channels;
15978 if (((ma_snd_pcm_hw_params_set_channels_near_proc)pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels) < 0) {
15979 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
15980 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
15981 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed.", MA_FORMAT_NOT_SUPPORTED);
15982 }
15983 internalChannels = (ma_uint32)channels;
15984 }
15985
15986 /* Sample Rate */
15987 {
15988 unsigned int sampleRate;
15989
15990 /*
15991 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
15992 problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable
15993 resampling.
15994
15995 To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a
15996 sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling
15997 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
15998 faster rate.
15999
16000 miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine
16001 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
16002 good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion.
16003
16004 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
16005 this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins.
16006 */
16007 ((ma_snd_pcm_hw_params_set_rate_resample_proc)pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0);
16008
16009 sampleRate = pConfig->sampleRate;
16010 if (((ma_snd_pcm_hw_params_set_rate_near_proc)pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0) < 0) {
16011 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
16012 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
16013 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed.", MA_FORMAT_NOT_SUPPORTED);
16014 }
16015 internalSampleRate = (ma_uint32)sampleRate;
16016 }
16017
16018 /* Periods. */
16019 {
16020 ma_uint32 periods = pConfig->periods;
16021 if (((ma_snd_pcm_hw_params_set_periods_near_proc)pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL) < 0) {
16022 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
16023 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
16024 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed.", MA_FORMAT_NOT_SUPPORTED);
16025 }
16026 internalPeriods = periods;
16027 }
16028
16029 /* Buffer Size */
16030 {
16031 ma_snd_pcm_uframes_t actualBufferSizeInFrames = pConfig->periodSizeInFrames * internalPeriods;
16032 if (actualBufferSizeInFrames == 0) {
16033 actualBufferSizeInFrames = ma_scale_buffer_size(ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, internalSampleRate), bufferSizeScaleFactor) * internalPeriods;
16034 }
16035
16036 if (((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames) < 0) {
16037 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
16038 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
16039 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_FORMAT_NOT_SUPPORTED);
16040 }
16041 internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods;
16042 }
16043
16044 /* Apply hardware parameters. */
16045 if (((ma_snd_pcm_hw_params_proc)pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams) < 0) {
16046 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
16047 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
16048 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
16049 }
16050
16051 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
16052 pHWParams = NULL;
16053
16054
16055 /* Software parameters. */
16056 pSWParams = (ma_snd_pcm_sw_params_t*)ma__calloc_from_callbacks(((ma_snd_pcm_sw_params_sizeof_proc)pContext->alsa.snd_pcm_sw_params_sizeof)(), &pContext->allocationCallbacks);
16057 if (pSWParams == NULL) {
16058 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
16059 return MA_OUT_OF_MEMORY;
16060 }
16061
16062 if (((ma_snd_pcm_sw_params_current_proc)pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams) != 0) {
16063 ma__free_from_callbacks(pSWParams, &pContext->allocationCallbacks);
16064 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
16065 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
16066 }
16067
16068 if (((ma_snd_pcm_sw_params_set_avail_min_proc)pContext->alsa.snd_pcm_sw_params_set_avail_min)(pPCM, pSWParams, ma_prev_power_of_2(internalPeriodSizeInFrames)) != 0) {
16069 ma__free_from_callbacks(pSWParams, &pContext->allocationCallbacks);
16070 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
16071 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.", MA_FORMAT_NOT_SUPPORTED);
16072 }
16073
16074 if (((ma_snd_pcm_sw_params_get_boundary_proc)pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary) < 0) {
16075 bufferBoundary = internalPeriodSizeInFrames * internalPeriods;
16076 }
16077
16078 /*printf("TRACE: bufferBoundary=%ld\n", bufferBoundary);*/
16079
16080 if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */
16081 /*
16082 Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to
16083 the size of a period. But for full-duplex we need to set it such that it is at least two periods.
16084 */
16085 if (((ma_snd_pcm_sw_params_set_start_threshold_proc)pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2) != 0) {
16086 ma__free_from_callbacks(pSWParams, &pContext->allocationCallbacks);
16087 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
16088 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_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
16089 }
16090 if (((ma_snd_pcm_sw_params_set_stop_threshold_proc)pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary) != 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */
16091 ma__free_from_callbacks(pSWParams, &pContext->allocationCallbacks);
16092 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
16093 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_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
16094 }
16095 }
16096
16097 if (((ma_snd_pcm_sw_params_proc)pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams) != 0) {
16098 ma__free_from_callbacks(pSWParams, &pContext->allocationCallbacks);
16099 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
16100 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
16101 }
16102
16103 ma__free_from_callbacks(pSWParams, &pContext->allocationCallbacks);
16104 pSWParams = NULL;
16105
16106
16107 /* 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. */
16108 {
16109 ma_snd_pcm_chmap_t* pChmap = ((ma_snd_pcm_get_chmap_proc)pContext->alsa.snd_pcm_get_chmap)(pPCM);
16110 if (pChmap != NULL) {
16111 ma_uint32 iChannel;
16112
16113 /* 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(). */
16114 if (pChmap->channels >= internalChannels) {
16115 /* Drop excess channels. */
16116 for (iChannel = 0; iChannel < internalChannels; ++iChannel) {
16117 internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
16118 }
16119 } else {
16120 ma_uint32 i;
16121
16122 /*
16123 Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate
16124 channels. If validation fails, fall back to defaults.
16125 */
16126 ma_bool32 isValid = MA_TRUE;
16127
16128 /* Fill with defaults. */
16129 ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
16130
16131 /* Overwrite first pChmap->channels channels. */
16132 for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) {
16133 internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
16134 }
16135
16136 /* Validate. */
16137 for (i = 0; i < internalChannels && isValid; ++i) {
16138 ma_uint32 j;
16139 for (j = i+1; j < internalChannels; ++j) {
16140 if (internalChannelMap[i] == internalChannelMap[j]) {
16141 isValid = MA_FALSE;
16142 break;
16143 }
16144 }
16145 }
16146
16147 /* If our channel map is invalid, fall back to defaults. */
16148 if (!isValid) {
16149 ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
16150 }
16151 }
16152
16153 free(pChmap);
16154 pChmap = NULL;
16155 } else {
16156 /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */
16157 ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
16158 }
16159 }
16160
16161
16162 /* We're done. Prepare the device. */
16163 if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM) < 0) {
16164 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
16165 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.", MA_FAILED_TO_START_BACKEND_DEVICE);
16166 }
16167
16168
16169 if (deviceType == ma_device_type_capture) {
16170 pDevice->alsa.pPCMCapture = (ma_ptr)pPCM;
16171 pDevice->alsa.isUsingMMapCapture = isUsingMMap;
16172 pDevice->capture.internalFormat = internalFormat;
16173 pDevice->capture.internalChannels = internalChannels;
16174 pDevice->capture.internalSampleRate = internalSampleRate;
16175 ma_channel_map_copy(pDevice->capture.internalChannelMap, internalChannelMap, internalChannels);
16176 pDevice->capture.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
16177 pDevice->capture.internalPeriods = internalPeriods;
16178 } else {
16179 pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM;
16180 pDevice->alsa.isUsingMMapPlayback = isUsingMMap;
16181 pDevice->playback.internalFormat = internalFormat;
16182 pDevice->playback.internalChannels = internalChannels;
16183 pDevice->playback.internalSampleRate = internalSampleRate;
16184 ma_channel_map_copy(pDevice->playback.internalChannelMap, internalChannelMap, internalChannels);
16185 pDevice->playback.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
16186 pDevice->playback.internalPeriods = internalPeriods;
16187 }
16188
16189 return MA_SUCCESS;
16190}
16191
16192static ma_result ma_device_init__alsa(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
16193{
16194 MA_ASSERT(pDevice != NULL);
16195
16196 MA_ZERO_OBJECT(&pDevice->alsa);
16197
16198 if (pConfig->deviceType == ma_device_type_loopback) {
16199 return MA_DEVICE_TYPE_NOT_SUPPORTED;
16200 }
16201
16202 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
16203 ma_result result = ma_device_init_by_type__alsa(pContext, pConfig, ma_device_type_capture, pDevice);
16204 if (result != MA_SUCCESS) {
16205 return result;
16206 }
16207 }
16208
16209 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
16210 ma_result result = ma_device_init_by_type__alsa(pContext, pConfig, ma_device_type_playback, pDevice);
16211 if (result != MA_SUCCESS) {
16212 return result;
16213 }
16214 }
16215
16216 return MA_SUCCESS;
16217}
16218
16219static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead)
16220{
16221 ma_snd_pcm_sframes_t resultALSA;
16222
16223 MA_ASSERT(pDevice != NULL);
16224 MA_ASSERT(pFramesOut != NULL);
16225
16226 if (pFramesRead != NULL) {
16227 *pFramesRead = 0;
16228 }
16229
16230 for (;;) {
16231 resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
16232 if (resultALSA >= 0) {
16233 break; /* Success. */
16234 } else {
16235 if (resultALSA == -EAGAIN) {
16236 /*printf("TRACE: EGAIN (read)\n");*/
16237 continue; /* Try again. */
16238 } else if (resultALSA == -EPIPE) {
16239 #if defined(MA_DEBUG_OUTPUT)
16240 printf("TRACE: EPIPE (read)\n");
16241 #endif
16242
16243 /* Overrun. Recover and try again. If this fails we need to return an error. */
16244 if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE) < 0) {
16245 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
16246 }
16247
16248 if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
16249 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
16250 }
16251
16252 resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
16253 if (resultALSA < 0) {
16254 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to read data from the internal device.", MA_FAILED_TO_READ_DATA_FROM_DEVICE);
16255 }
16256 }
16257 }
16258 }
16259
16260 if (pFramesRead != NULL) {
16261 *pFramesRead = resultALSA;
16262 }
16263
16264 return MA_SUCCESS;
16265}
16266
16267static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
16268{
16269 ma_snd_pcm_sframes_t resultALSA;
16270
16271 MA_ASSERT(pDevice != NULL);
16272 MA_ASSERT(pFrames != NULL);
16273
16274 if (pFramesWritten != NULL) {
16275 *pFramesWritten = 0;
16276 }
16277
16278 for (;;) {
16279 resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);
16280 if (resultALSA >= 0) {
16281 break; /* Success. */
16282 } else {
16283 if (resultALSA == -EAGAIN) {
16284 /*printf("TRACE: EGAIN (write)\n");*/
16285 continue; /* Try again. */
16286 } else if (resultALSA == -EPIPE) {
16287 #if defined(MA_DEBUG_OUTPUT)
16288 printf("TRACE: EPIPE (write)\n");
16289 #endif
16290
16291 /* Underrun. Recover and try again. If this fails we need to return an error. */
16292 if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE) < 0) { /* MA_TRUE=silent (don't print anything on error). */
16293 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
16294 }
16295
16296 /*
16297 In my testing I have had a situation where writei() does not automatically restart the device even though I've set it
16298 up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of
16299 frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure
16300 if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't
16301 quite right here.
16302 */
16303 if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) {
16304 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
16305 }
16306
16307 resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);
16308 if (resultALSA < 0) {
16309 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to write data to device after underrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
16310 }
16311 }
16312 }
16313 }
16314
16315 if (pFramesWritten != NULL) {
16316 *pFramesWritten = resultALSA;
16317 }
16318
16319 return MA_SUCCESS;
16320}
16321
16322static ma_result ma_device_main_loop__alsa(ma_device* pDevice)
16323{
16324 ma_result result = MA_SUCCESS;
16325 ma_bool32 exitLoop = MA_FALSE;
16326
16327 MA_ASSERT(pDevice != NULL);
16328
16329 /* Capture devices need to be started immediately. */
16330 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
16331 if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
16332 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device in preparation for reading.", MA_FAILED_TO_START_BACKEND_DEVICE);
16333 }
16334 }
16335
16336 while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
16337 switch (pDevice->type)
16338 {
16339 case ma_device_type_duplex:
16340 {
16341 if (pDevice->alsa.isUsingMMapCapture || pDevice->alsa.isUsingMMapPlayback) {
16342 /* MMAP */
16343 return MA_INVALID_OPERATION; /* Not yet implemented. */
16344 } else {
16345 /* readi() and writei() */
16346
16347 /* The process is: device_read -> convert -> callback -> convert -> device_write */
16348 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
16349 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
16350
16351 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
16352 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
16353 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
16354 ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
16355 ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
16356 ma_uint32 capturedDeviceFramesRemaining;
16357 ma_uint32 capturedDeviceFramesProcessed;
16358 ma_uint32 capturedDeviceFramesToProcess;
16359 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
16360 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
16361 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
16362 }
16363
16364 result = ma_device_read__alsa(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
16365 if (result != MA_SUCCESS) {
16366 exitLoop = MA_TRUE;
16367 break;
16368 }
16369
16370 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
16371 capturedDeviceFramesProcessed = 0;
16372
16373 for (;;) {
16374 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
16375 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
16376 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
16377 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
16378 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
16379 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
16380 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
16381
16382 /* Convert capture data from device format to client format. */
16383 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
16384 if (result != MA_SUCCESS) {
16385 break;
16386 }
16387
16388 /*
16389 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
16390 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
16391 */
16392 if (capturedClientFramesToProcessThisIteration == 0) {
16393 break;
16394 }
16395
16396 ma_device__on_data(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
16397
16398 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
16399 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
16400
16401 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
16402 for (;;) {
16403 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
16404 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
16405 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
16406 if (result != MA_SUCCESS) {
16407 break;
16408 }
16409
16410 result = ma_device_write__alsa(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
16411 if (result != MA_SUCCESS) {
16412 exitLoop = MA_TRUE;
16413 break;
16414 }
16415
16416 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
16417 if (capturedClientFramesToProcessThisIteration == 0) {
16418 break;
16419 }
16420 }
16421
16422 /* In case an error happened from ma_device_write__alsa()... */
16423 if (result != MA_SUCCESS) {
16424 exitLoop = MA_TRUE;
16425 break;
16426 }
16427 }
16428
16429 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
16430 }
16431 }
16432 } break;
16433
16434 case ma_device_type_capture:
16435 {
16436 if (pDevice->alsa.isUsingMMapCapture) {
16437 /* MMAP */
16438 return MA_INVALID_OPERATION; /* Not yet implemented. */
16439 } else {
16440 /* readi() */
16441
16442 /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
16443 ma_uint8 intermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
16444 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
16445 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
16446 ma_uint32 framesReadThisPeriod = 0;
16447 while (framesReadThisPeriod < periodSizeInFrames) {
16448 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
16449 ma_uint32 framesProcessed;
16450 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
16451 if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
16452 framesToReadThisIteration = intermediaryBufferSizeInFrames;
16453 }
16454
16455 result = ma_device_read__alsa(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
16456 if (result != MA_SUCCESS) {
16457 exitLoop = MA_TRUE;
16458 break;
16459 }
16460
16461 ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
16462
16463 framesReadThisPeriod += framesProcessed;
16464 }
16465 }
16466 } break;
16467
16468 case ma_device_type_playback:
16469 {
16470 if (pDevice->alsa.isUsingMMapPlayback) {
16471 /* MMAP */
16472 return MA_INVALID_OPERATION; /* Not yet implemented. */
16473 } else {
16474 /* writei() */
16475
16476 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
16477 ma_uint8 intermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
16478 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
16479 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
16480 ma_uint32 framesWrittenThisPeriod = 0;
16481 while (framesWrittenThisPeriod < periodSizeInFrames) {
16482 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
16483 ma_uint32 framesProcessed;
16484 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
16485 if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
16486 framesToWriteThisIteration = intermediaryBufferSizeInFrames;
16487 }
16488
16489 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
16490
16491 result = ma_device_write__alsa(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
16492 if (result != MA_SUCCESS) {
16493 exitLoop = MA_TRUE;
16494 break;
16495 }
16496
16497 framesWrittenThisPeriod += framesProcessed;
16498 }
16499 }
16500 } break;
16501
16502 /* To silence a warning. Will never hit this. */
16503 case ma_device_type_loopback:
16504 default: break;
16505 }
16506 }
16507
16508 /* Here is where the device needs to be stopped. */
16509 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
16510 ((ma_snd_pcm_drain_proc)pDevice->pContext->alsa.snd_pcm_drain)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
16511
16512 /* We need to prepare the device again, otherwise we won't be able to restart the device. */
16513 if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
16514 #ifdef MA_DEBUG_OUTPUT
16515 printf("[ALSA] Failed to prepare capture device after stopping.\n");
16516 #endif
16517 }
16518 }
16519
16520 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
16521 ((ma_snd_pcm_drain_proc)pDevice->pContext->alsa.snd_pcm_drain)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
16522
16523 /* We need to prepare the device again, otherwise we won't be able to restart the device. */
16524 if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) {
16525 #ifdef MA_DEBUG_OUTPUT
16526 printf("[ALSA] Failed to prepare playback device after stopping.\n");
16527 #endif
16528 }
16529 }
16530
16531 return result;
16532}
16533
16534static ma_result ma_context_uninit__alsa(ma_context* pContext)
16535{
16536 MA_ASSERT(pContext != NULL);
16537 MA_ASSERT(pContext->backend == ma_backend_alsa);
16538
16539 /* Clean up memory for memory leak checkers. */
16540 ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)();
16541
16542#ifndef MA_NO_RUNTIME_LINKING
16543 ma_dlclose(pContext, pContext->alsa.asoundSO);
16544#endif
16545
16546 ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock);
16547
16548 return MA_SUCCESS;
16549}
16550
16551static ma_result ma_context_init__alsa(const ma_context_config* pConfig, ma_context* pContext)
16552{
16553#ifndef MA_NO_RUNTIME_LINKING
16554 const char* libasoundNames[] = {
16555 "libasound.so.2",
16556 "libasound.so"
16557 };
16558 size_t i;
16559
16560 for (i = 0; i < ma_countof(libasoundNames); ++i) {
16561 pContext->alsa.asoundSO = ma_dlopen(pContext, libasoundNames[i]);
16562 if (pContext->alsa.asoundSO != NULL) {
16563 break;
16564 }
16565 }
16566
16567 if (pContext->alsa.asoundSO == NULL) {
16568#ifdef MA_DEBUG_OUTPUT
16569 printf("[ALSA] Failed to open shared object.\n");
16570#endif
16571 return MA_NO_BACKEND;
16572 }
16573
16574 pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_open");
16575 pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_close");
16576 pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof");
16577 pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_any");
16578 pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format");
16579 pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first");
16580 pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask");
16581 pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near");
16582 pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample");
16583 pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near");
16584 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");
16585 pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near");
16586 pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access");
16587 pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format");
16588 pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels");
16589 pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min");
16590 pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max");
16591 pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate");
16592 pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min");
16593 pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max");
16594 pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size");
16595 pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods");
16596 pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access");
16597 pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params");
16598 pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof");
16599 pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_current");
16600 pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary");
16601 pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min");
16602 pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold");
16603 pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold");
16604 pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params");
16605 pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof");
16606 pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_test");
16607 pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_get_chmap");
16608 pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_state");
16609 pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_prepare");
16610 pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_start");
16611 pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drop");
16612 pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drain");
16613 pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_hint");
16614 pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_get_hint");
16615 pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_card_get_index");
16616 pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_free_hint");
16617 pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_begin");
16618 pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_commit");
16619 pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_recover");
16620 pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_readi");
16621 pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_writei");
16622 pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail");
16623 pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail_update");
16624 pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_wait");
16625 pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info");
16626 pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_sizeof");
16627 pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_get_name");
16628 pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_config_update_free_global");
16629#else
16630 /* The system below is just for type safety. */
16631 ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open;
16632 ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close;
16633 ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof;
16634 ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any;
16635 ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format;
16636 ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first;
16637 ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask;
16638 ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near;
16639 ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample;
16640 ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near;
16641 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;
16642 ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near;
16643 ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access;
16644 ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format;
16645 ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels;
16646 ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min;
16647 ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max;
16648 ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate;
16649 ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min;
16650 ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max;
16651 ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size;
16652 ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods;
16653 ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access;
16654 ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params;
16655 ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof;
16656 ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current;
16657 ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary;
16658 ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min;
16659 ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold;
16660 ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold;
16661 ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params;
16662 ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof;
16663 ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test;
16664 ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap;
16665 ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state;
16666 ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare;
16667 ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start;
16668 ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop;
16669 ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain;
16670 ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint;
16671 ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint;
16672 ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index;
16673 ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint;
16674 ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin;
16675 ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit;
16676 ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover;
16677 ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi;
16678 ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei;
16679 ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail;
16680 ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update;
16681 ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait;
16682 ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info;
16683 ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof;
16684 ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name;
16685 ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global;
16686
16687 pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open;
16688 pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close;
16689 pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof;
16690 pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any;
16691 pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format;
16692 pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first;
16693 pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask;
16694 pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near;
16695 pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample;
16696 pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near;
16697 pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near;
16698 pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near;
16699 pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access;
16700 pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format;
16701 pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels;
16702 pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min;
16703 pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max;
16704 pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate;
16705 pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size;
16706 pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods;
16707 pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access;
16708 pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params;
16709 pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof;
16710 pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current;
16711 pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary;
16712 pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min;
16713 pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold;
16714 pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold;
16715 pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params;
16716 pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof;
16717 pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test;
16718 pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap;
16719 pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state;
16720 pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare;
16721 pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start;
16722 pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop;
16723 pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain;
16724 pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint;
16725 pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint;
16726 pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index;
16727 pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint;
16728 pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin;
16729 pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit;
16730 pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover;
16731 pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi;
16732 pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei;
16733 pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail;
16734 pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update;
16735 pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait;
16736 pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info;
16737 pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof;
16738 pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name;
16739 pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global;
16740#endif
16741
16742 pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration;
16743
16744 if (ma_mutex_init(pContext, &pContext->alsa.internalDeviceEnumLock) != MA_SUCCESS) {
16745 ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration.", MA_ERROR);
16746 }
16747
16748 pContext->onUninit = ma_context_uninit__alsa;
16749 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__alsa;
16750 pContext->onEnumDevices = ma_context_enumerate_devices__alsa;
16751 pContext->onGetDeviceInfo = ma_context_get_device_info__alsa;
16752 pContext->onDeviceInit = ma_device_init__alsa;
16753 pContext->onDeviceUninit = ma_device_uninit__alsa;
16754 pContext->onDeviceStart = NULL; /* Not used. Started in the main loop. */
16755 pContext->onDeviceStop = NULL; /* Not used. Started in the main loop. */
16756 pContext->onDeviceMainLoop = ma_device_main_loop__alsa;
16757
16758 return MA_SUCCESS;
16759}
16760#endif /* ALSA */
16761
16762
16763
16764/******************************************************************************
16765
16766PulseAudio Backend
16767
16768******************************************************************************/
16769#ifdef MA_HAS_PULSEAUDIO
16770/*
16771It is assumed pulseaudio.h is available when compile-time linking is being used. We use this for type safety when using
16772compile time linking (we don't have this luxury when using runtime linking without headers).
16773
16774When using compile time linking, each of our ma_* equivalents should use the sames types as defined by the header. The
16775reason for this is that it allow us to take advantage of proper type safety.
16776*/
16777#ifdef MA_NO_RUNTIME_LINKING
16778#include <pulse/pulseaudio.h>
16779
16780#define MA_PA_OK PA_OK
16781#define MA_PA_ERR_ACCESS PA_ERR_ACCESS
16782#define MA_PA_ERR_INVALID PA_ERR_INVALID
16783#define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY
16784
16785#define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX
16786#define MA_PA_RATE_MAX PA_RATE_MAX
16787
16788typedef pa_context_flags_t ma_pa_context_flags_t;
16789#define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS
16790#define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN
16791#define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL
16792
16793typedef pa_stream_flags_t ma_pa_stream_flags_t;
16794#define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS
16795#define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED
16796#define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING
16797#define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC
16798#define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE
16799#define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS
16800#define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS
16801#define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT
16802#define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE
16803#define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS
16804#define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE
16805#define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE
16806#define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT
16807#define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED
16808#define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY
16809#define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS
16810#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
16811#define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED
16812#define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
16813#define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
16814#define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
16815
16816typedef pa_sink_flags_t ma_pa_sink_flags_t;
16817#define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS
16818#define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL
16819#define MA_PA_SINK_LATENCY PA_SINK_LATENCY
16820#define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE
16821#define MA_PA_SINK_NETWORK PA_SINK_NETWORK
16822#define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL
16823#define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
16824#define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME
16825#define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY
16826#define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS
16827
16828typedef pa_source_flags_t ma_pa_source_flags_t;
16829#define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS
16830#define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL
16831#define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY
16832#define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE
16833#define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK
16834#define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL
16835#define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME
16836#define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY
16837#define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME
16838
16839typedef pa_context_state_t ma_pa_context_state_t;
16840#define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED
16841#define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING
16842#define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING
16843#define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME
16844#define MA_PA_CONTEXT_READY PA_CONTEXT_READY
16845#define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED
16846#define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED
16847
16848typedef pa_stream_state_t ma_pa_stream_state_t;
16849#define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED
16850#define MA_PA_STREAM_CREATING PA_STREAM_CREATING
16851#define MA_PA_STREAM_READY PA_STREAM_READY
16852#define MA_PA_STREAM_FAILED PA_STREAM_FAILED
16853#define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED
16854
16855typedef pa_operation_state_t ma_pa_operation_state_t;
16856#define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING
16857#define MA_PA_OPERATION_DONE PA_OPERATION_DONE
16858#define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED
16859
16860typedef pa_sink_state_t ma_pa_sink_state_t;
16861#define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE
16862#define MA_PA_SINK_RUNNING PA_SINK_RUNNING
16863#define MA_PA_SINK_IDLE PA_SINK_IDLE
16864#define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED
16865
16866typedef pa_source_state_t ma_pa_source_state_t;
16867#define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE
16868#define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING
16869#define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE
16870#define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED
16871
16872typedef pa_seek_mode_t ma_pa_seek_mode_t;
16873#define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE
16874#define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE
16875#define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ
16876#define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END
16877
16878typedef pa_channel_position_t ma_pa_channel_position_t;
16879#define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID
16880#define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO
16881#define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT
16882#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT
16883#define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER
16884#define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER
16885#define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT
16886#define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT
16887#define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE
16888#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
16889#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
16890#define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT
16891#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT
16892#define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0
16893#define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1
16894#define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2
16895#define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3
16896#define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4
16897#define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5
16898#define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6
16899#define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7
16900#define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8
16901#define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9
16902#define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10
16903#define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11
16904#define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12
16905#define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13
16906#define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14
16907#define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15
16908#define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16
16909#define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17
16910#define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18
16911#define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19
16912#define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20
16913#define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21
16914#define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22
16915#define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23
16916#define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24
16917#define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25
16918#define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26
16919#define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27
16920#define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28
16921#define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29
16922#define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30
16923#define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31
16924#define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER
16925#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT
16926#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
16927#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER
16928#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT
16929#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT
16930#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER
16931#define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT
16932#define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT
16933#define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER
16934#define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER
16935
16936typedef pa_channel_map_def_t ma_pa_channel_map_def_t;
16937#define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF
16938#define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA
16939#define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX
16940#define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX
16941#define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS
16942#define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT
16943
16944typedef pa_sample_format_t ma_pa_sample_format_t;
16945#define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID
16946#define MA_PA_SAMPLE_U8 PA_SAMPLE_U8
16947#define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW
16948#define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW
16949#define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE
16950#define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE
16951#define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE
16952#define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE
16953#define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE
16954#define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE
16955#define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE
16956#define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE
16957#define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE
16958#define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE
16959
16960typedef pa_mainloop ma_pa_mainloop;
16961typedef pa_mainloop_api ma_pa_mainloop_api;
16962typedef pa_context ma_pa_context;
16963typedef pa_operation ma_pa_operation;
16964typedef pa_stream ma_pa_stream;
16965typedef pa_spawn_api ma_pa_spawn_api;
16966typedef pa_buffer_attr ma_pa_buffer_attr;
16967typedef pa_channel_map ma_pa_channel_map;
16968typedef pa_cvolume ma_pa_cvolume;
16969typedef pa_sample_spec ma_pa_sample_spec;
16970typedef pa_sink_info ma_pa_sink_info;
16971typedef pa_source_info ma_pa_source_info;
16972
16973typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t;
16974typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t;
16975typedef pa_source_info_cb_t ma_pa_source_info_cb_t;
16976typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t;
16977typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t;
16978typedef pa_free_cb_t ma_pa_free_cb_t;
16979#else
16980#define MA_PA_OK 0
16981#define MA_PA_ERR_ACCESS 1
16982#define MA_PA_ERR_INVALID 2
16983#define MA_PA_ERR_NOENTITY 5
16984
16985#define MA_PA_CHANNELS_MAX 32
16986#define MA_PA_RATE_MAX 384000
16987
16988typedef int ma_pa_context_flags_t;
16989#define MA_PA_CONTEXT_NOFLAGS 0x00000000
16990#define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001
16991#define MA_PA_CONTEXT_NOFAIL 0x00000002
16992
16993typedef int ma_pa_stream_flags_t;
16994#define MA_PA_STREAM_NOFLAGS 0x00000000
16995#define MA_PA_STREAM_START_CORKED 0x00000001
16996#define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002
16997#define MA_PA_STREAM_NOT_MONOTONIC 0x00000004
16998#define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008
16999#define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010
17000#define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020
17001#define MA_PA_STREAM_FIX_FORMAT 0x00000040
17002#define MA_PA_STREAM_FIX_RATE 0x00000080
17003#define MA_PA_STREAM_FIX_CHANNELS 0x00000100
17004#define MA_PA_STREAM_DONT_MOVE 0x00000200
17005#define MA_PA_STREAM_VARIABLE_RATE 0x00000400
17006#define MA_PA_STREAM_PEAK_DETECT 0x00000800
17007#define MA_PA_STREAM_START_MUTED 0x00001000
17008#define MA_PA_STREAM_ADJUST_LATENCY 0x00002000
17009#define MA_PA_STREAM_EARLY_REQUESTS 0x00004000
17010#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000
17011#define MA_PA_STREAM_START_UNMUTED 0x00010000
17012#define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000
17013#define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000
17014#define MA_PA_STREAM_PASSTHROUGH 0x00080000
17015
17016typedef int ma_pa_sink_flags_t;
17017#define MA_PA_SINK_NOFLAGS 0x00000000
17018#define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001
17019#define MA_PA_SINK_LATENCY 0x00000002
17020#define MA_PA_SINK_HARDWARE 0x00000004
17021#define MA_PA_SINK_NETWORK 0x00000008
17022#define MA_PA_SINK_HW_MUTE_CTRL 0x00000010
17023#define MA_PA_SINK_DECIBEL_VOLUME 0x00000020
17024#define MA_PA_SINK_FLAT_VOLUME 0x00000040
17025#define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080
17026#define MA_PA_SINK_SET_FORMATS 0x00000100
17027
17028typedef int ma_pa_source_flags_t;
17029#define MA_PA_SOURCE_NOFLAGS 0x00000000
17030#define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001
17031#define MA_PA_SOURCE_LATENCY 0x00000002
17032#define MA_PA_SOURCE_HARDWARE 0x00000004
17033#define MA_PA_SOURCE_NETWORK 0x00000008
17034#define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010
17035#define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020
17036#define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040
17037#define MA_PA_SOURCE_FLAT_VOLUME 0x00000080
17038
17039typedef int ma_pa_context_state_t;
17040#define MA_PA_CONTEXT_UNCONNECTED 0
17041#define MA_PA_CONTEXT_CONNECTING 1
17042#define MA_PA_CONTEXT_AUTHORIZING 2
17043#define MA_PA_CONTEXT_SETTING_NAME 3
17044#define MA_PA_CONTEXT_READY 4
17045#define MA_PA_CONTEXT_FAILED 5
17046#define MA_PA_CONTEXT_TERMINATED 6
17047
17048typedef int ma_pa_stream_state_t;
17049#define MA_PA_STREAM_UNCONNECTED 0
17050#define MA_PA_STREAM_CREATING 1
17051#define MA_PA_STREAM_READY 2
17052#define MA_PA_STREAM_FAILED 3
17053#define MA_PA_STREAM_TERMINATED 4
17054
17055typedef int ma_pa_operation_state_t;
17056#define MA_PA_OPERATION_RUNNING 0
17057#define MA_PA_OPERATION_DONE 1
17058#define MA_PA_OPERATION_CANCELLED 2
17059
17060typedef int ma_pa_sink_state_t;
17061#define MA_PA_SINK_INVALID_STATE -1
17062#define MA_PA_SINK_RUNNING 0
17063#define MA_PA_SINK_IDLE 1
17064#define MA_PA_SINK_SUSPENDED 2
17065
17066typedef int ma_pa_source_state_t;
17067#define MA_PA_SOURCE_INVALID_STATE -1
17068#define MA_PA_SOURCE_RUNNING 0
17069#define MA_PA_SOURCE_IDLE 1
17070#define MA_PA_SOURCE_SUSPENDED 2
17071
17072typedef int ma_pa_seek_mode_t;
17073#define MA_PA_SEEK_RELATIVE 0
17074#define MA_PA_SEEK_ABSOLUTE 1
17075#define MA_PA_SEEK_RELATIVE_ON_READ 2
17076#define MA_PA_SEEK_RELATIVE_END 3
17077
17078typedef int ma_pa_channel_position_t;
17079#define MA_PA_CHANNEL_POSITION_INVALID -1
17080#define MA_PA_CHANNEL_POSITION_MONO 0
17081#define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1
17082#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2
17083#define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3
17084#define MA_PA_CHANNEL_POSITION_REAR_CENTER 4
17085#define MA_PA_CHANNEL_POSITION_REAR_LEFT 5
17086#define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6
17087#define MA_PA_CHANNEL_POSITION_LFE 7
17088#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8
17089#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9
17090#define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10
17091#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11
17092#define MA_PA_CHANNEL_POSITION_AUX0 12
17093#define MA_PA_CHANNEL_POSITION_AUX1 13
17094#define MA_PA_CHANNEL_POSITION_AUX2 14
17095#define MA_PA_CHANNEL_POSITION_AUX3 15
17096#define MA_PA_CHANNEL_POSITION_AUX4 16
17097#define MA_PA_CHANNEL_POSITION_AUX5 17
17098#define MA_PA_CHANNEL_POSITION_AUX6 18
17099#define MA_PA_CHANNEL_POSITION_AUX7 19
17100#define MA_PA_CHANNEL_POSITION_AUX8 20
17101#define MA_PA_CHANNEL_POSITION_AUX9 21
17102#define MA_PA_CHANNEL_POSITION_AUX10 22
17103#define MA_PA_CHANNEL_POSITION_AUX11 23
17104#define MA_PA_CHANNEL_POSITION_AUX12 24
17105#define MA_PA_CHANNEL_POSITION_AUX13 25
17106#define MA_PA_CHANNEL_POSITION_AUX14 26
17107#define MA_PA_CHANNEL_POSITION_AUX15 27
17108#define MA_PA_CHANNEL_POSITION_AUX16 28
17109#define MA_PA_CHANNEL_POSITION_AUX17 29
17110#define MA_PA_CHANNEL_POSITION_AUX18 30
17111#define MA_PA_CHANNEL_POSITION_AUX19 31
17112#define MA_PA_CHANNEL_POSITION_AUX20 32
17113#define MA_PA_CHANNEL_POSITION_AUX21 33
17114#define MA_PA_CHANNEL_POSITION_AUX22 34
17115#define MA_PA_CHANNEL_POSITION_AUX23 35
17116#define MA_PA_CHANNEL_POSITION_AUX24 36
17117#define MA_PA_CHANNEL_POSITION_AUX25 37
17118#define MA_PA_CHANNEL_POSITION_AUX26 38
17119#define MA_PA_CHANNEL_POSITION_AUX27 39
17120#define MA_PA_CHANNEL_POSITION_AUX28 40
17121#define MA_PA_CHANNEL_POSITION_AUX29 41
17122#define MA_PA_CHANNEL_POSITION_AUX30 42
17123#define MA_PA_CHANNEL_POSITION_AUX31 43
17124#define MA_PA_CHANNEL_POSITION_TOP_CENTER 44
17125#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45
17126#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46
17127#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47
17128#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48
17129#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49
17130#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50
17131#define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT
17132#define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT
17133#define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER
17134#define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE
17135
17136typedef int ma_pa_channel_map_def_t;
17137#define MA_PA_CHANNEL_MAP_AIFF 0
17138#define MA_PA_CHANNEL_MAP_ALSA 1
17139#define MA_PA_CHANNEL_MAP_AUX 2
17140#define MA_PA_CHANNEL_MAP_WAVEEX 3
17141#define MA_PA_CHANNEL_MAP_OSS 4
17142#define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF
17143
17144typedef int ma_pa_sample_format_t;
17145#define MA_PA_SAMPLE_INVALID -1
17146#define MA_PA_SAMPLE_U8 0
17147#define MA_PA_SAMPLE_ALAW 1
17148#define MA_PA_SAMPLE_ULAW 2
17149#define MA_PA_SAMPLE_S16LE 3
17150#define MA_PA_SAMPLE_S16BE 4
17151#define MA_PA_SAMPLE_FLOAT32LE 5
17152#define MA_PA_SAMPLE_FLOAT32BE 6
17153#define MA_PA_SAMPLE_S32LE 7
17154#define MA_PA_SAMPLE_S32BE 8
17155#define MA_PA_SAMPLE_S24LE 9
17156#define MA_PA_SAMPLE_S24BE 10
17157#define MA_PA_SAMPLE_S24_32LE 11
17158#define MA_PA_SAMPLE_S24_32BE 12
17159
17160typedef struct ma_pa_mainloop ma_pa_mainloop;
17161typedef struct ma_pa_mainloop_api ma_pa_mainloop_api;
17162typedef struct ma_pa_context ma_pa_context;
17163typedef struct ma_pa_operation ma_pa_operation;
17164typedef struct ma_pa_stream ma_pa_stream;
17165typedef struct ma_pa_spawn_api ma_pa_spawn_api;
17166
17167typedef struct
17168{
17169 ma_uint32 maxlength;
17170 ma_uint32 tlength;
17171 ma_uint32 prebuf;
17172 ma_uint32 minreq;
17173 ma_uint32 fragsize;
17174} ma_pa_buffer_attr;
17175
17176typedef struct
17177{
17178 ma_uint8 channels;
17179 ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX];
17180} ma_pa_channel_map;
17181
17182typedef struct
17183{
17184 ma_uint8 channels;
17185 ma_uint32 values[MA_PA_CHANNELS_MAX];
17186} ma_pa_cvolume;
17187
17188typedef struct
17189{
17190 ma_pa_sample_format_t format;
17191 ma_uint32 rate;
17192 ma_uint8 channels;
17193} ma_pa_sample_spec;
17194
17195typedef struct
17196{
17197 const char* name;
17198 ma_uint32 index;
17199 const char* description;
17200 ma_pa_sample_spec sample_spec;
17201 ma_pa_channel_map channel_map;
17202 ma_uint32 owner_module;
17203 ma_pa_cvolume volume;
17204 int mute;
17205 ma_uint32 monitor_source;
17206 const char* monitor_source_name;
17207 ma_uint64 latency;
17208 const char* driver;
17209 ma_pa_sink_flags_t flags;
17210 void* proplist;
17211 ma_uint64 configured_latency;
17212 ma_uint32 base_volume;
17213 ma_pa_sink_state_t state;
17214 ma_uint32 n_volume_steps;
17215 ma_uint32 card;
17216 ma_uint32 n_ports;
17217 void** ports;
17218 void* active_port;
17219 ma_uint8 n_formats;
17220 void** formats;
17221} ma_pa_sink_info;
17222
17223typedef struct
17224{
17225 const char *name;
17226 ma_uint32 index;
17227 const char *description;
17228 ma_pa_sample_spec sample_spec;
17229 ma_pa_channel_map channel_map;
17230 ma_uint32 owner_module;
17231 ma_pa_cvolume volume;
17232 int mute;
17233 ma_uint32 monitor_of_sink;
17234 const char *monitor_of_sink_name;
17235 ma_uint64 latency;
17236 const char *driver;
17237 ma_pa_source_flags_t flags;
17238 void* proplist;
17239 ma_uint64 configured_latency;
17240 ma_uint32 base_volume;
17241 ma_pa_source_state_t state;
17242 ma_uint32 n_volume_steps;
17243 ma_uint32 card;
17244 ma_uint32 n_ports;
17245 void** ports;
17246 void* active_port;
17247 ma_uint8 n_formats;
17248 void** formats;
17249} ma_pa_source_info;
17250
17251typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata);
17252typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata);
17253typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata);
17254typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata);
17255typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata);
17256typedef void (* ma_pa_free_cb_t) (void* p);
17257#endif
17258
17259
17260typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) ();
17261typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m);
17262typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m);
17263typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval);
17264typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m);
17265typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name);
17266typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c);
17267typedef 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);
17268typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c);
17269typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata);
17270typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c);
17271typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata);
17272typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata);
17273typedef 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);
17274typedef 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);
17275typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o);
17276typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o);
17277typedef 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);
17278typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m);
17279typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss);
17280typedef 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);
17281typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s);
17282typedef 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);
17283typedef 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);
17284typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s);
17285typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s);
17286typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s);
17287typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s);
17288typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s);
17289typedef 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);
17290typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s);
17291typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
17292typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
17293typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
17294typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
17295typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s);
17296typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata);
17297typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
17298typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes);
17299typedef 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);
17300typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes);
17301typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s);
17302typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s);
17303typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s);
17304
17305typedef struct
17306{
17307 ma_uint32 count;
17308 ma_uint32 capacity;
17309 ma_device_info* pInfo;
17310} ma_pulse_device_enum_data;
17311
17312static ma_result ma_result_from_pulse(int result)
17313{
17314 switch (result) {
17315 case MA_PA_OK: return MA_SUCCESS;
17316 case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED;
17317 case MA_PA_ERR_INVALID: return MA_INVALID_ARGS;
17318 case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE;
17319 default: return MA_ERROR;
17320 }
17321}
17322
17323#if 0
17324static ma_pa_sample_format_t ma_format_to_pulse(ma_format format)
17325{
17326 if (ma_is_little_endian()) {
17327 switch (format) {
17328 case ma_format_s16: return MA_PA_SAMPLE_S16LE;
17329 case ma_format_s24: return MA_PA_SAMPLE_S24LE;
17330 case ma_format_s32: return MA_PA_SAMPLE_S32LE;
17331 case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE;
17332 default: break;
17333 }
17334 } else {
17335 switch (format) {
17336 case ma_format_s16: return MA_PA_SAMPLE_S16BE;
17337 case ma_format_s24: return MA_PA_SAMPLE_S24BE;
17338 case ma_format_s32: return MA_PA_SAMPLE_S32BE;
17339 case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE;
17340 default: break;
17341 }
17342 }
17343
17344 /* Endian agnostic. */
17345 switch (format) {
17346 case ma_format_u8: return MA_PA_SAMPLE_U8;
17347 default: return MA_PA_SAMPLE_INVALID;
17348 }
17349}
17350#endif
17351
17352static ma_format ma_format_from_pulse(ma_pa_sample_format_t format)
17353{
17354 if (ma_is_little_endian()) {
17355 switch (format) {
17356 case MA_PA_SAMPLE_S16LE: return ma_format_s16;
17357 case MA_PA_SAMPLE_S24LE: return ma_format_s24;
17358 case MA_PA_SAMPLE_S32LE: return ma_format_s32;
17359 case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32;
17360 default: break;
17361 }
17362 } else {
17363 switch (format) {
17364 case MA_PA_SAMPLE_S16BE: return ma_format_s16;
17365 case MA_PA_SAMPLE_S24BE: return ma_format_s24;
17366 case MA_PA_SAMPLE_S32BE: return ma_format_s32;
17367 case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32;
17368 default: break;
17369 }
17370 }
17371
17372 /* Endian agnostic. */
17373 switch (format) {
17374 case MA_PA_SAMPLE_U8: return ma_format_u8;
17375 default: return ma_format_unknown;
17376 }
17377}
17378
17379static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position)
17380{
17381 switch (position)
17382 {
17383 case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE;
17384 case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO;
17385 case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
17386 case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
17387 case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
17388 case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER;
17389 case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT;
17390 case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT;
17391 case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE;
17392 case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
17393 case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
17394 case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
17395 case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
17396 case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0;
17397 case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1;
17398 case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2;
17399 case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3;
17400 case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4;
17401 case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5;
17402 case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6;
17403 case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7;
17404 case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8;
17405 case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9;
17406 case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10;
17407 case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11;
17408 case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12;
17409 case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13;
17410 case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14;
17411 case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15;
17412 case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16;
17413 case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17;
17414 case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18;
17415 case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19;
17416 case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20;
17417 case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21;
17418 case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22;
17419 case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23;
17420 case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24;
17421 case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25;
17422 case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26;
17423 case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27;
17424 case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28;
17425 case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29;
17426 case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30;
17427 case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31;
17428 case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
17429 case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
17430 case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
17431 case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
17432 case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
17433 case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
17434 case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
17435 default: return MA_CHANNEL_NONE;
17436 }
17437}
17438
17439#if 0
17440static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position)
17441{
17442 switch (position)
17443 {
17444 case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID;
17445 case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT;
17446 case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT;
17447 case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER;
17448 case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE;
17449 case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT;
17450 case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT;
17451 case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
17452 case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
17453 case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER;
17454 case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT;
17455 case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT;
17456 case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER;
17457 case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
17458 case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
17459 case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
17460 case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT;
17461 case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER;
17462 case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
17463 case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18;
17464 case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19;
17465 case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20;
17466 case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21;
17467 case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22;
17468 case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23;
17469 case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24;
17470 case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25;
17471 case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26;
17472 case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27;
17473 case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28;
17474 case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29;
17475 case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30;
17476 case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31;
17477 default: return (ma_pa_channel_position_t)position;
17478 }
17479}
17480#endif
17481
17482static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_mainloop* pMainLoop, ma_pa_operation* pOP)
17483{
17484 MA_ASSERT(pContext != NULL);
17485 MA_ASSERT(pMainLoop != NULL);
17486 MA_ASSERT(pOP != NULL);
17487
17488 while (((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP) == MA_PA_OPERATION_RUNNING) {
17489 int error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL);
17490 if (error < 0) {
17491 return ma_result_from_pulse(error);
17492 }
17493 }
17494
17495 return MA_SUCCESS;
17496}
17497
17498static ma_result ma_device__wait_for_operation__pulse(ma_device* pDevice, ma_pa_operation* pOP)
17499{
17500 MA_ASSERT(pDevice != NULL);
17501 MA_ASSERT(pOP != NULL);
17502
17503 return ma_wait_for_operation__pulse(pDevice->pContext, (ma_pa_mainloop*)pDevice->pulse.pMainLoop, pOP);
17504}
17505
17506
17507static ma_bool32 ma_context_is_device_id_equal__pulse(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
17508{
17509 MA_ASSERT(pContext != NULL);
17510 MA_ASSERT(pID0 != NULL);
17511 MA_ASSERT(pID1 != NULL);
17512 (void)pContext;
17513
17514 return ma_strcmp(pID0->pulse, pID1->pulse) == 0;
17515}
17516
17517
17518typedef struct
17519{
17520 ma_context* pContext;
17521 ma_enum_devices_callback_proc callback;
17522 void* pUserData;
17523 ma_bool32 isTerminated;
17524} ma_context_enumerate_devices_callback_data__pulse;
17525
17526static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData)
17527{
17528 ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
17529 ma_device_info deviceInfo;
17530
17531 MA_ASSERT(pData != NULL);
17532
17533 if (endOfList || pData->isTerminated) {
17534 return;
17535 }
17536
17537 MA_ZERO_OBJECT(&deviceInfo);
17538
17539 /* The name from PulseAudio is the ID for miniaudio. */
17540 if (pSinkInfo->name != NULL) {
17541 ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1);
17542 }
17543
17544 /* The description from PulseAudio is the name for miniaudio. */
17545 if (pSinkInfo->description != NULL) {
17546 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1);
17547 }
17548
17549 pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData);
17550
17551 (void)pPulseContext; /* Unused. */
17552}
17553
17554static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSinkInfo, int endOfList, void* pUserData)
17555{
17556 ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
17557 ma_device_info deviceInfo;
17558
17559 MA_ASSERT(pData != NULL);
17560
17561 if (endOfList || pData->isTerminated) {
17562 return;
17563 }
17564
17565 MA_ZERO_OBJECT(&deviceInfo);
17566
17567 /* The name from PulseAudio is the ID for miniaudio. */
17568 if (pSinkInfo->name != NULL) {
17569 ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1);
17570 }
17571
17572 /* The description from PulseAudio is the name for miniaudio. */
17573 if (pSinkInfo->description != NULL) {
17574 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1);
17575 }
17576
17577 pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData);
17578
17579 (void)pPulseContext; /* Unused. */
17580}
17581
17582static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
17583{
17584 ma_result result = MA_SUCCESS;
17585 ma_context_enumerate_devices_callback_data__pulse callbackData;
17586 ma_pa_operation* pOP = NULL;
17587 ma_pa_mainloop* pMainLoop;
17588 ma_pa_mainloop_api* pAPI;
17589 ma_pa_context* pPulseContext;
17590 int error;
17591
17592 MA_ASSERT(pContext != NULL);
17593 MA_ASSERT(callback != NULL);
17594
17595 callbackData.pContext = pContext;
17596 callbackData.callback = callback;
17597 callbackData.pUserData = pUserData;
17598 callbackData.isTerminated = MA_FALSE;
17599
17600 pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
17601 if (pMainLoop == NULL) {
17602 return MA_FAILED_TO_INIT_BACKEND;
17603 }
17604
17605 pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)(pMainLoop);
17606 if (pAPI == NULL) {
17607 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
17608 return MA_FAILED_TO_INIT_BACKEND;
17609 }
17610
17611 pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(pAPI, pContext->pulse.pApplicationName);
17612 if (pPulseContext == NULL) {
17613 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
17614 return MA_FAILED_TO_INIT_BACKEND;
17615 }
17616
17617 error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)(pPulseContext, pContext->pulse.pServerName, (pContext->pulse.tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL);
17618 if (error != MA_PA_OK) {
17619 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
17620 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
17621 return ma_result_from_pulse(error);
17622 }
17623
17624 for (;;) {
17625 ma_pa_context_state_t state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext);
17626 if (state == MA_PA_CONTEXT_READY) {
17627 break; /* Success. */
17628 }
17629 if (state == MA_PA_CONTEXT_CONNECTING || state == MA_PA_CONTEXT_AUTHORIZING || state == MA_PA_CONTEXT_SETTING_NAME) {
17630 error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL);
17631 if (error < 0) {
17632 result = ma_result_from_pulse(error);
17633 goto done;
17634 }
17635
17636#ifdef MA_DEBUG_OUTPUT
17637 printf("[PulseAudio] pa_context_get_state() returned %d. Waiting.\n", state);
17638#endif
17639 continue; /* Keep trying. */
17640 }
17641 if (state == MA_PA_CONTEXT_UNCONNECTED || state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {
17642#ifdef MA_DEBUG_OUTPUT
17643 printf("[PulseAudio] pa_context_get_state() returned %d. Failed.\n", state);
17644#endif
17645 goto done; /* Failed. */
17646 }
17647 }
17648
17649
17650 /* Playback. */
17651 if (!callbackData.isTerminated) {
17652 pOP = ((ma_pa_context_get_sink_info_list_proc)pContext->pulse.pa_context_get_sink_info_list)(pPulseContext, ma_context_enumerate_devices_sink_callback__pulse, &callbackData);
17653 if (pOP == NULL) {
17654 result = MA_ERROR;
17655 goto done;
17656 }
17657
17658 result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
17659 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
17660 if (result != MA_SUCCESS) {
17661 goto done;
17662 }
17663 }
17664
17665
17666 /* Capture. */
17667 if (!callbackData.isTerminated) {
17668 pOP = ((ma_pa_context_get_source_info_list_proc)pContext->pulse.pa_context_get_source_info_list)(pPulseContext, ma_context_enumerate_devices_source_callback__pulse, &callbackData);
17669 if (pOP == NULL) {
17670 result = MA_ERROR;
17671 goto done;
17672 }
17673
17674 result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
17675 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
17676 if (result != MA_SUCCESS) {
17677 goto done;
17678 }
17679 }
17680
17681done:
17682 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)(pPulseContext);
17683 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
17684 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
17685 return result;
17686}
17687
17688
17689typedef struct
17690{
17691 ma_device_info* pDeviceInfo;
17692 ma_bool32 foundDevice;
17693} ma_context_get_device_info_callback_data__pulse;
17694
17695static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
17696{
17697 ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
17698
17699 if (endOfList > 0) {
17700 return;
17701 }
17702
17703 MA_ASSERT(pData != NULL);
17704 pData->foundDevice = MA_TRUE;
17705
17706 if (pInfo->name != NULL) {
17707 ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
17708 }
17709
17710 if (pInfo->description != NULL) {
17711 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
17712 }
17713
17714 pData->pDeviceInfo->minChannels = pInfo->sample_spec.channels;
17715 pData->pDeviceInfo->maxChannels = pInfo->sample_spec.channels;
17716 pData->pDeviceInfo->minSampleRate = pInfo->sample_spec.rate;
17717 pData->pDeviceInfo->maxSampleRate = pInfo->sample_spec.rate;
17718 pData->pDeviceInfo->formatCount = 1;
17719 pData->pDeviceInfo->formats[0] = ma_format_from_pulse(pInfo->sample_spec.format);
17720
17721 (void)pPulseContext; /* Unused. */
17722}
17723
17724static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
17725{
17726 ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
17727
17728 if (endOfList > 0) {
17729 return;
17730 }
17731
17732 MA_ASSERT(pData != NULL);
17733 pData->foundDevice = MA_TRUE;
17734
17735 if (pInfo->name != NULL) {
17736 ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
17737 }
17738
17739 if (pInfo->description != NULL) {
17740 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
17741 }
17742
17743 pData->pDeviceInfo->minChannels = pInfo->sample_spec.channels;
17744 pData->pDeviceInfo->maxChannels = pInfo->sample_spec.channels;
17745 pData->pDeviceInfo->minSampleRate = pInfo->sample_spec.rate;
17746 pData->pDeviceInfo->maxSampleRate = pInfo->sample_spec.rate;
17747 pData->pDeviceInfo->formatCount = 1;
17748 pData->pDeviceInfo->formats[0] = ma_format_from_pulse(pInfo->sample_spec.format);
17749
17750 (void)pPulseContext; /* Unused. */
17751}
17752
17753static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
17754{
17755 ma_result result = MA_SUCCESS;
17756 ma_context_get_device_info_callback_data__pulse callbackData;
17757 ma_pa_operation* pOP = NULL;
17758 ma_pa_mainloop* pMainLoop;
17759 ma_pa_mainloop_api* pAPI;
17760 ma_pa_context* pPulseContext;
17761 int error;
17762
17763 MA_ASSERT(pContext != NULL);
17764
17765 /* No exclusive mode with the PulseAudio backend. */
17766 if (shareMode == ma_share_mode_exclusive) {
17767 return MA_SHARE_MODE_NOT_SUPPORTED;
17768 }
17769
17770 callbackData.pDeviceInfo = pDeviceInfo;
17771 callbackData.foundDevice = MA_FALSE;
17772
17773 pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
17774 if (pMainLoop == NULL) {
17775 return MA_FAILED_TO_INIT_BACKEND;
17776 }
17777
17778 pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)(pMainLoop);
17779 if (pAPI == NULL) {
17780 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
17781 return MA_FAILED_TO_INIT_BACKEND;
17782 }
17783
17784 pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(pAPI, pContext->pulse.pApplicationName);
17785 if (pPulseContext == NULL) {
17786 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
17787 return MA_FAILED_TO_INIT_BACKEND;
17788 }
17789
17790 error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)(pPulseContext, pContext->pulse.pServerName, 0, NULL);
17791 if (error != MA_PA_OK) {
17792 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
17793 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
17794 return ma_result_from_pulse(error);
17795 }
17796
17797 for (;;) {
17798 ma_pa_context_state_t state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext);
17799 if (state == MA_PA_CONTEXT_READY) {
17800 break; /* Success. */
17801 }
17802 if (state == MA_PA_CONTEXT_CONNECTING || state == MA_PA_CONTEXT_AUTHORIZING || state == MA_PA_CONTEXT_SETTING_NAME) {
17803 error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL);
17804 if (error < 0) {
17805 result = ma_result_from_pulse(error);
17806 goto done;
17807 }
17808
17809#ifdef MA_DEBUG_OUTPUT
17810 printf("[PulseAudio] pa_context_get_state() returned %d. Waiting.\n", state);
17811#endif
17812 continue; /* Keep trying. */
17813 }
17814 if (state == MA_PA_CONTEXT_UNCONNECTED || state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {
17815#ifdef MA_DEBUG_OUTPUT
17816 printf("[PulseAudio] pa_context_get_state() returned %d. Failed.\n", state);
17817#endif
17818 goto done; /* Failed. */
17819 }
17820 }
17821
17822 if (deviceType == ma_device_type_playback) {
17823 pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)(pPulseContext, pDeviceID->pulse, ma_context_get_device_info_sink_callback__pulse, &callbackData);
17824 } else {
17825 pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)(pPulseContext, pDeviceID->pulse, ma_context_get_device_info_source_callback__pulse, &callbackData);
17826 }
17827
17828 if (pOP != NULL) {
17829 ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
17830 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
17831 } else {
17832 result = MA_ERROR;
17833 goto done;
17834 }
17835
17836 if (!callbackData.foundDevice) {
17837 result = MA_NO_DEVICE;
17838 goto done;
17839 }
17840
17841
17842done:
17843 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)(pPulseContext);
17844 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
17845 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
17846 return result;
17847}
17848
17849
17850static void ma_pulse_device_state_callback(ma_pa_context* pPulseContext, void* pUserData)
17851{
17852 ma_device* pDevice;
17853 ma_context* pContext;
17854
17855 pDevice = (ma_device*)pUserData;
17856 MA_ASSERT(pDevice != NULL);
17857
17858 pContext = pDevice->pContext;
17859 MA_ASSERT(pContext != NULL);
17860
17861 pDevice->pulse.pulseContextState = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext);
17862}
17863
17864void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
17865{
17866 ma_pa_sink_info* pInfoOut;
17867
17868 if (endOfList > 0) {
17869 return;
17870 }
17871
17872 pInfoOut = (ma_pa_sink_info*)pUserData;
17873 MA_ASSERT(pInfoOut != NULL);
17874
17875 *pInfoOut = *pInfo;
17876
17877 (void)pPulseContext; /* Unused. */
17878}
17879
17880static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
17881{
17882 ma_pa_source_info* pInfoOut;
17883
17884 if (endOfList > 0) {
17885 return;
17886 }
17887
17888 pInfoOut = (ma_pa_source_info*)pUserData;
17889 MA_ASSERT(pInfoOut != NULL);
17890
17891 *pInfoOut = *pInfo;
17892
17893 (void)pPulseContext; /* Unused. */
17894}
17895
17896static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
17897{
17898 ma_device* pDevice;
17899
17900 if (endOfList > 0) {
17901 return;
17902 }
17903
17904 pDevice = (ma_device*)pUserData;
17905 MA_ASSERT(pDevice != NULL);
17906
17907 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1);
17908
17909 (void)pPulseContext; /* Unused. */
17910}
17911
17912static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
17913{
17914 ma_device* pDevice;
17915
17916 if (endOfList > 0) {
17917 return;
17918 }
17919
17920 pDevice = (ma_device*)pUserData;
17921 MA_ASSERT(pDevice != NULL);
17922
17923 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1);
17924
17925 (void)pPulseContext; /* Unused. */
17926}
17927
17928static void ma_device_uninit__pulse(ma_device* pDevice)
17929{
17930 ma_context* pContext;
17931
17932 MA_ASSERT(pDevice != NULL);
17933
17934 pContext = pDevice->pContext;
17935 MA_ASSERT(pContext != NULL);
17936
17937 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
17938 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
17939 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
17940 }
17941 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
17942 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
17943 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
17944 }
17945
17946 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext);
17947 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext);
17948 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
17949}
17950
17951static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss)
17952{
17953 ma_pa_buffer_attr attr;
17954 attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels);
17955 attr.tlength = attr.maxlength / periods;
17956 attr.prebuf = (ma_uint32)-1;
17957 attr.minreq = (ma_uint32)-1;
17958 attr.fragsize = attr.maxlength / periods;
17959
17960 return attr;
17961}
17962
17963static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap)
17964{
17965 static int g_StreamCounter = 0;
17966 char actualStreamName[256];
17967
17968 if (pStreamName != NULL) {
17969 ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1);
17970 } else {
17971 ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:");
17972 ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */
17973 }
17974 g_StreamCounter += 1;
17975
17976 return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap);
17977}
17978
17979static ma_result ma_device_init__pulse(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
17980{
17981 ma_result result = MA_SUCCESS;
17982 int error = 0;
17983 const char* devPlayback = NULL;
17984 const char* devCapture = NULL;
17985 ma_uint32 periodSizeInMilliseconds;
17986 ma_pa_sink_info sinkInfo;
17987 ma_pa_source_info sourceInfo;
17988 ma_pa_operation* pOP = NULL;
17989 ma_pa_sample_spec ss;
17990 ma_pa_channel_map cmap;
17991 ma_pa_buffer_attr attr;
17992 const ma_pa_sample_spec* pActualSS = NULL;
17993 const ma_pa_channel_map* pActualCMap = NULL;
17994 const ma_pa_buffer_attr* pActualAttr = NULL;
17995 ma_uint32 iChannel;
17996 ma_pa_stream_flags_t streamFlags;
17997
17998 MA_ASSERT(pDevice != NULL);
17999 MA_ZERO_OBJECT(&pDevice->pulse);
18000
18001 if (pConfig->deviceType == ma_device_type_loopback) {
18002 return MA_DEVICE_TYPE_NOT_SUPPORTED;
18003 }
18004
18005 /* No exclusive mode with the PulseAudio backend. */
18006 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
18007 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
18008 return MA_SHARE_MODE_NOT_SUPPORTED;
18009 }
18010
18011 if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID != NULL) {
18012 devPlayback = pConfig->playback.pDeviceID->pulse;
18013 }
18014 if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID != NULL) {
18015 devCapture = pConfig->capture.pDeviceID->pulse;
18016 }
18017
18018 periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
18019 if (periodSizeInMilliseconds == 0) {
18020 periodSizeInMilliseconds = ma_calculate_buffer_size_in_milliseconds_from_frames(pConfig->periodSizeInFrames, pConfig->sampleRate);
18021 }
18022
18023 pDevice->pulse.pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
18024 if (pDevice->pulse.pMainLoop == NULL) {
18025 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create main loop for device.", MA_FAILED_TO_INIT_BACKEND);
18026 goto on_error0;
18027 }
18028
18029 pDevice->pulse.pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
18030 if (pDevice->pulse.pAPI == NULL) {
18031 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve PulseAudio main loop.", MA_FAILED_TO_INIT_BACKEND);
18032 goto on_error1;
18033 }
18034
18035 pDevice->pulse.pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)((ma_pa_mainloop_api*)pDevice->pulse.pAPI, pContext->pulse.pApplicationName);
18036 if (pDevice->pulse.pPulseContext == NULL) {
18037 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context for device.", MA_FAILED_TO_INIT_BACKEND);
18038 goto on_error1;
18039 }
18040
18041 error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pDevice->pulse.pPulseContext, pContext->pulse.pServerName, (pContext->pulse.tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL);
18042 if (error != MA_PA_OK) {
18043 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context.", ma_result_from_pulse(error));
18044 goto on_error2;
18045 }
18046
18047
18048 pDevice->pulse.pulseContextState = MA_PA_CONTEXT_UNCONNECTED;
18049 ((ma_pa_context_set_state_callback_proc)pContext->pulse.pa_context_set_state_callback)((ma_pa_context*)pDevice->pulse.pPulseContext, ma_pulse_device_state_callback, pDevice);
18050
18051 /* Wait for PulseAudio to get itself ready before returning. */
18052 for (;;) {
18053 if (pDevice->pulse.pulseContextState == MA_PA_CONTEXT_READY) {
18054 break;
18055 }
18056
18057 /* An error may have occurred. */
18058 if (pDevice->pulse.pulseContextState == MA_PA_CONTEXT_FAILED || pDevice->pulse.pulseContextState == MA_PA_CONTEXT_TERMINATED) {
18059 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context.", MA_ERROR);
18060 goto on_error3;
18061 }
18062
18063 error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
18064 if (error < 0) {
18065 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] The PulseAudio main loop returned an error while connecting the PulseAudio context.", ma_result_from_pulse(error));
18066 goto on_error3;
18067 }
18068 }
18069
18070 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
18071 pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devCapture, ma_device_source_info_callback, &sourceInfo);
18072 if (pOP != NULL) {
18073 ma_device__wait_for_operation__pulse(pDevice, pOP);
18074 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
18075 } else {
18076 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device.", ma_result_from_pulse(error));
18077 goto on_error3;
18078 }
18079
18080 ss = sourceInfo.sample_spec;
18081 cmap = sourceInfo.channel_map;
18082
18083 pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, ss.rate);
18084 pDevice->capture.internalPeriods = pConfig->periods;
18085
18086 attr = ma_device__pa_buffer_attr_new(pDevice->capture.internalPeriodSizeInFrames, pConfig->periods, &ss);
18087 #ifdef MA_DEBUG_OUTPUT
18088 printf("[PulseAudio] Capture 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->capture.internalPeriodSizeInFrames);
18089 #endif
18090
18091 pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap);
18092 if (pDevice->pulse.pStreamCapture == NULL) {
18093 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
18094 goto on_error3;
18095 }
18096
18097 streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
18098 if (devCapture != NULL) {
18099 streamFlags |= MA_PA_STREAM_DONT_MOVE;
18100 }
18101
18102 error = ((ma_pa_stream_connect_record_proc)pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags);
18103 if (error != MA_PA_OK) {
18104 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream.", ma_result_from_pulse(error));
18105 goto on_error4;
18106 }
18107
18108 while (((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pDevice->pulse.pStreamCapture) != MA_PA_STREAM_READY) {
18109 error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
18110 if (error < 0) {
18111 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] The PulseAudio main loop returned an error while connecting the PulseAudio capture stream.", ma_result_from_pulse(error));
18112 goto on_error5;
18113 }
18114 }
18115
18116 /* Internal format. */
18117 pActualSS = ((ma_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
18118 if (pActualSS != NULL) {
18119 /* If anything has changed between the requested and the actual sample spec, we need to update the buffer. */
18120 if (ss.format != pActualSS->format || ss.channels != pActualSS->channels || ss.rate != pActualSS->rate) {
18121 attr = ma_device__pa_buffer_attr_new(pDevice->capture.internalPeriodSizeInFrames, pConfig->periods, pActualSS);
18122
18123 pOP = ((ma_pa_stream_set_buffer_attr_proc)pContext->pulse.pa_stream_set_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture, &attr, NULL, NULL);
18124 if (pOP != NULL) {
18125 ma_device__wait_for_operation__pulse(pDevice, pOP);
18126 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
18127 }
18128 }
18129
18130 ss = *pActualSS;
18131 }
18132
18133 pDevice->capture.internalFormat = ma_format_from_pulse(ss.format);
18134 pDevice->capture.internalChannels = ss.channels;
18135 pDevice->capture.internalSampleRate = ss.rate;
18136
18137 /* Internal channel map. */
18138 pActualCMap = ((ma_pa_stream_get_channel_map_proc)pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
18139 if (pActualCMap != NULL) {
18140 cmap = *pActualCMap;
18141 }
18142 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
18143 pDevice->capture.internalChannelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
18144 }
18145
18146 /* Buffer. */
18147 pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
18148 if (pActualAttr != NULL) {
18149 attr = *pActualAttr;
18150 }
18151 pDevice->capture.internalPeriods = attr.maxlength / attr.fragsize;
18152 pDevice->capture.internalPeriodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels) / pDevice->capture.internalPeriods;
18153 #ifdef MA_DEBUG_OUTPUT
18154 printf("[PulseAudio] Capture 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->capture.internalPeriodSizeInFrames);
18155 #endif
18156
18157 /* Name. */
18158 devCapture = ((ma_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
18159 if (devCapture != NULL) {
18160 ma_pa_operation* pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devCapture, ma_device_source_name_callback, pDevice);
18161 if (pOP != NULL) {
18162 ma_device__wait_for_operation__pulse(pDevice, pOP);
18163 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
18164 }
18165 }
18166 }
18167
18168 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
18169 pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devPlayback, ma_device_sink_info_callback, &sinkInfo);
18170 if (pOP != NULL) {
18171 ma_device__wait_for_operation__pulse(pDevice, pOP);
18172 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
18173 } else {
18174 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.", ma_result_from_pulse(error));
18175 goto on_error3;
18176 }
18177
18178 ss = sinkInfo.sample_spec;
18179 cmap = sinkInfo.channel_map;
18180
18181 pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, ss.rate);
18182 pDevice->playback.internalPeriods = pConfig->periods;
18183
18184 attr = ma_device__pa_buffer_attr_new(pDevice->playback.internalPeriodSizeInFrames, pConfig->periods, &ss);
18185 #ifdef MA_DEBUG_OUTPUT
18186 printf("[PulseAudio] Playback 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);
18187 #endif
18188
18189 pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap);
18190 if (pDevice->pulse.pStreamPlayback == NULL) {
18191 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
18192 goto on_error3;
18193 }
18194
18195 streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
18196 if (devPlayback != NULL) {
18197 streamFlags |= MA_PA_STREAM_DONT_MOVE;
18198 }
18199
18200 error = ((ma_pa_stream_connect_playback_proc)pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL);
18201 if (error != MA_PA_OK) {
18202 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream.", ma_result_from_pulse(error));
18203 goto on_error6;
18204 }
18205
18206 while (((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pDevice->pulse.pStreamPlayback) != MA_PA_STREAM_READY) {
18207 error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
18208 if (error < 0) {
18209 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] The PulseAudio main loop returned an error while connecting the PulseAudio playback stream.", ma_result_from_pulse(error));
18210 goto on_error7;
18211 }
18212 }
18213
18214 /* Internal format. */
18215 pActualSS = ((ma_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
18216 if (pActualSS != NULL) {
18217 /* If anything has changed between the requested and the actual sample spec, we need to update the buffer. */
18218 if (ss.format != pActualSS->format || ss.channels != pActualSS->channels || ss.rate != pActualSS->rate) {
18219 attr = ma_device__pa_buffer_attr_new(pDevice->playback.internalPeriodSizeInFrames, pConfig->periods, pActualSS);
18220
18221 pOP = ((ma_pa_stream_set_buffer_attr_proc)pContext->pulse.pa_stream_set_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, &attr, NULL, NULL);
18222 if (pOP != NULL) {
18223 ma_device__wait_for_operation__pulse(pDevice, pOP);
18224 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
18225 }
18226 }
18227
18228 ss = *pActualSS;
18229 }
18230
18231 pDevice->playback.internalFormat = ma_format_from_pulse(ss.format);
18232 pDevice->playback.internalChannels = ss.channels;
18233 pDevice->playback.internalSampleRate = ss.rate;
18234
18235 /* Internal channel map. */
18236 pActualCMap = ((ma_pa_stream_get_channel_map_proc)pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
18237 if (pActualCMap != NULL) {
18238 cmap = *pActualCMap;
18239 }
18240 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
18241 pDevice->playback.internalChannelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
18242 }
18243
18244 /* Buffer. */
18245 pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
18246 if (pActualAttr != NULL) {
18247 attr = *pActualAttr;
18248 }
18249 pDevice->playback.internalPeriods = attr.maxlength / attr.tlength;
18250 pDevice->playback.internalPeriodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels) / pDevice->playback.internalPeriods;
18251 #ifdef MA_DEBUG_OUTPUT
18252 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);
18253 #endif
18254
18255 /* Name. */
18256 devPlayback = ((ma_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
18257 if (devPlayback != NULL) {
18258 ma_pa_operation* pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devPlayback, ma_device_sink_name_callback, pDevice);
18259 if (pOP != NULL) {
18260 ma_device__wait_for_operation__pulse(pDevice, pOP);
18261 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
18262 }
18263 }
18264 }
18265
18266 return MA_SUCCESS;
18267
18268
18269on_error7:
18270 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
18271 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
18272 }
18273on_error6:
18274 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
18275 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
18276 }
18277on_error5:
18278 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
18279 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
18280 }
18281on_error4:
18282 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
18283 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
18284 }
18285on_error3: ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext);
18286on_error2: ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext);
18287on_error1: ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
18288on_error0:
18289 return result;
18290}
18291
18292
18293static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData)
18294{
18295 ma_bool32* pIsSuccessful = (ma_bool32*)pUserData;
18296 MA_ASSERT(pIsSuccessful != NULL);
18297
18298 *pIsSuccessful = (ma_bool32)success;
18299
18300 (void)pStream; /* Unused. */
18301}
18302
18303static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork)
18304{
18305 ma_context* pContext = pDevice->pContext;
18306 ma_bool32 wasSuccessful;
18307 ma_pa_stream* pStream;
18308 ma_pa_operation* pOP;
18309 ma_result result;
18310
18311 /* This should not be called with a duplex device type. */
18312 if (deviceType == ma_device_type_duplex) {
18313 return MA_INVALID_ARGS;
18314 }
18315
18316 wasSuccessful = MA_FALSE;
18317
18318 pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback);
18319 MA_ASSERT(pStream != NULL);
18320
18321 pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful);
18322 if (pOP == NULL) {
18323 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);
18324 }
18325
18326 result = ma_device__wait_for_operation__pulse(pDevice, pOP);
18327 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
18328
18329 if (result != MA_SUCCESS) {
18330 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork.", result);
18331 }
18332
18333 if (!wasSuccessful) {
18334 if (cork) {
18335 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to stop PulseAudio stream.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
18336 } else {
18337 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to start PulseAudio stream.", MA_FAILED_TO_START_BACKEND_DEVICE);
18338 }
18339 }
18340
18341 return MA_SUCCESS;
18342}
18343
18344static ma_result ma_device_stop__pulse(ma_device* pDevice)
18345{
18346 ma_result result;
18347 ma_bool32 wasSuccessful;
18348 ma_pa_operation* pOP;
18349
18350 MA_ASSERT(pDevice != NULL);
18351
18352 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
18353 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1);
18354 if (result != MA_SUCCESS) {
18355 return result;
18356 }
18357 }
18358
18359 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
18360 /* The stream needs to be drained if it's a playback device. */
18361 pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful);
18362 if (pOP != NULL) {
18363 ma_device__wait_for_operation__pulse(pDevice, pOP);
18364 ((ma_pa_operation_unref_proc)pDevice->pContext->pulse.pa_operation_unref)(pOP);
18365 }
18366
18367 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1);
18368 if (result != MA_SUCCESS) {
18369 return result;
18370 }
18371 }
18372
18373 return MA_SUCCESS;
18374}
18375
18376static ma_result ma_device_write__pulse(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
18377{
18378 ma_uint32 totalFramesWritten;
18379
18380 MA_ASSERT(pDevice != NULL);
18381 MA_ASSERT(pPCMFrames != NULL);
18382 MA_ASSERT(frameCount > 0);
18383
18384 if (pFramesWritten != NULL) {
18385 *pFramesWritten = 0;
18386 }
18387
18388 totalFramesWritten = 0;
18389 while (totalFramesWritten < frameCount) {
18390 if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
18391 return MA_DEVICE_NOT_STARTED;
18392 }
18393
18394 /* Place the data into the mapped buffer if we have one. */
18395 if (pDevice->pulse.pMappedBufferPlayback != NULL && pDevice->pulse.mappedBufferFramesRemainingPlayback > 0) {
18396 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
18397 ma_uint32 mappedBufferFramesConsumed = pDevice->pulse.mappedBufferFramesCapacityPlayback - pDevice->pulse.mappedBufferFramesRemainingPlayback;
18398
18399 void* pDst = (ma_uint8*)pDevice->pulse.pMappedBufferPlayback + (mappedBufferFramesConsumed * bpf);
18400 const void* pSrc = (const ma_uint8*)pPCMFrames + (totalFramesWritten * bpf);
18401 ma_uint32 framesToCopy = ma_min(pDevice->pulse.mappedBufferFramesRemainingPlayback, (frameCount - totalFramesWritten));
18402 MA_COPY_MEMORY(pDst, pSrc, framesToCopy * bpf);
18403
18404 pDevice->pulse.mappedBufferFramesRemainingPlayback -= framesToCopy;
18405 totalFramesWritten += framesToCopy;
18406 }
18407
18408 /*
18409 Getting here means we've run out of data in the currently mapped chunk. We need to write this to the device and then try
18410 mapping another chunk. If this fails we need to wait for space to become available.
18411 */
18412 if (pDevice->pulse.mappedBufferFramesCapacityPlayback > 0 && pDevice->pulse.mappedBufferFramesRemainingPlayback == 0) {
18413 size_t nbytes = pDevice->pulse.mappedBufferFramesCapacityPlayback * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
18414
18415 int error = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, pDevice->pulse.pMappedBufferPlayback, nbytes, NULL, 0, MA_PA_SEEK_RELATIVE);
18416 if (error < 0) {
18417 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to write data to the PulseAudio stream.", ma_result_from_pulse(error));
18418 }
18419
18420 pDevice->pulse.pMappedBufferPlayback = NULL;
18421 pDevice->pulse.mappedBufferFramesRemainingPlayback = 0;
18422 pDevice->pulse.mappedBufferFramesCapacityPlayback = 0;
18423 }
18424
18425 MA_ASSERT(totalFramesWritten <= frameCount);
18426 if (totalFramesWritten == frameCount) {
18427 break;
18428 }
18429
18430 /* Getting here means we need to map a new buffer. If we don't have enough space we need to wait for more. */
18431 for (;;) {
18432 size_t writableSizeInBytes;
18433
18434 /* If the device has been corked, don't try to continue. */
18435 if (((ma_pa_stream_is_corked_proc)pDevice->pContext->pulse.pa_stream_is_corked)((ma_pa_stream*)pDevice->pulse.pStreamPlayback)) {
18436 break;
18437 }
18438
18439 writableSizeInBytes = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
18440 if (writableSizeInBytes != (size_t)-1) {
18441 if (writableSizeInBytes > 0) {
18442 /* Data is avaialable. */
18443 size_t bytesToMap = writableSizeInBytes;
18444 int error = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, &pDevice->pulse.pMappedBufferPlayback, &bytesToMap);
18445 if (error < 0) {
18446 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to map write buffer.", ma_result_from_pulse(error));
18447 }
18448
18449 pDevice->pulse.mappedBufferFramesCapacityPlayback = bytesToMap / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
18450 pDevice->pulse.mappedBufferFramesRemainingPlayback = pDevice->pulse.mappedBufferFramesCapacityPlayback;
18451
18452 break;
18453 } else {
18454 /* No data available. Need to wait for more. */
18455 int error = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
18456 if (error < 0) {
18457 return ma_result_from_pulse(error);
18458 }
18459
18460 continue;
18461 }
18462 } else {
18463 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to query the stream's writable size.", MA_ERROR);
18464 }
18465 }
18466 }
18467
18468 if (pFramesWritten != NULL) {
18469 *pFramesWritten = totalFramesWritten;
18470 }
18471
18472 return MA_SUCCESS;
18473}
18474
18475static ma_result ma_device_read__pulse(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
18476{
18477 ma_uint32 totalFramesRead;
18478
18479 MA_ASSERT(pDevice != NULL);
18480 MA_ASSERT(pPCMFrames != NULL);
18481 MA_ASSERT(frameCount > 0);
18482
18483 if (pFramesRead != NULL) {
18484 *pFramesRead = 0;
18485 }
18486
18487 totalFramesRead = 0;
18488 while (totalFramesRead < frameCount) {
18489 if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
18490 return MA_DEVICE_NOT_STARTED;
18491 }
18492
18493 /*
18494 If a buffer is mapped we need to read from that first. Once it's consumed we need to drop it. Note that pDevice->pulse.pMappedBufferCapture can be null in which
18495 case it could be a hole. In this case we just write zeros into the output buffer.
18496 */
18497 if (pDevice->pulse.mappedBufferFramesRemainingCapture > 0) {
18498 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
18499 ma_uint32 mappedBufferFramesConsumed = pDevice->pulse.mappedBufferFramesCapacityCapture - pDevice->pulse.mappedBufferFramesRemainingCapture;
18500
18501 ma_uint32 framesToCopy = ma_min(pDevice->pulse.mappedBufferFramesRemainingCapture, (frameCount - totalFramesRead));
18502 void* pDst = (ma_uint8*)pPCMFrames + (totalFramesRead * bpf);
18503
18504 /*
18505 This little bit of logic here is specifically for PulseAudio and it's hole management. The buffer pointer will be set to NULL
18506 when the current fragment is a hole. For a hole we just output silence.
18507 */
18508 if (pDevice->pulse.pMappedBufferCapture != NULL) {
18509 const void* pSrc = (const ma_uint8*)pDevice->pulse.pMappedBufferCapture + (mappedBufferFramesConsumed * bpf);
18510 MA_COPY_MEMORY(pDst, pSrc, framesToCopy * bpf);
18511 } else {
18512 MA_ZERO_MEMORY(pDst, framesToCopy * bpf);
18513 #if defined(MA_DEBUG_OUTPUT)
18514 printf("[PulseAudio] ma_device_read__pulse: Filling hole with silence.\n");
18515 #endif
18516 }
18517
18518 pDevice->pulse.mappedBufferFramesRemainingCapture -= framesToCopy;
18519 totalFramesRead += framesToCopy;
18520 }
18521
18522 /*
18523 Getting here means we've run out of data in the currently mapped chunk. We need to drop this from the device and then try
18524 mapping another chunk. If this fails we need to wait for data to become available.
18525 */
18526 if (pDevice->pulse.mappedBufferFramesCapacityCapture > 0 && pDevice->pulse.mappedBufferFramesRemainingCapture == 0) {
18527 int error;
18528
18529 #if defined(MA_DEBUG_OUTPUT)
18530 printf("[PulseAudio] ma_device_read__pulse: Call pa_stream_drop()\n");
18531 #endif
18532
18533 error = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
18534 if (error != 0) {
18535 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to drop fragment.", ma_result_from_pulse(error));
18536 }
18537
18538 pDevice->pulse.pMappedBufferCapture = NULL;
18539 pDevice->pulse.mappedBufferFramesRemainingCapture = 0;
18540 pDevice->pulse.mappedBufferFramesCapacityCapture = 0;
18541 }
18542
18543 MA_ASSERT(totalFramesRead <= frameCount);
18544 if (totalFramesRead == frameCount) {
18545 break;
18546 }
18547
18548 /* Getting here means we need to map a new buffer. If we don't have enough data we wait for more. */
18549 for (;;) {
18550 int error;
18551 size_t bytesMapped;
18552
18553 if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
18554 break;
18555 }
18556
18557 /* If the device has been corked, don't try to continue. */
18558 if (((ma_pa_stream_is_corked_proc)pDevice->pContext->pulse.pa_stream_is_corked)((ma_pa_stream*)pDevice->pulse.pStreamCapture)) {
18559 #if defined(MA_DEBUG_OUTPUT)
18560 printf("[PulseAudio] ma_device_read__pulse: Corked.\n");
18561 #endif
18562 break;
18563 }
18564
18565 MA_ASSERT(pDevice->pulse.pMappedBufferCapture == NULL); /* <-- We're about to map a buffer which means we shouldn't have an existing mapping. */
18566
18567 error = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)((ma_pa_stream*)pDevice->pulse.pStreamCapture, &pDevice->pulse.pMappedBufferCapture, &bytesMapped);
18568 if (error < 0) {
18569 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to peek capture buffer.", ma_result_from_pulse(error));
18570 }
18571
18572 if (bytesMapped > 0) {
18573 pDevice->pulse.mappedBufferFramesCapacityCapture = bytesMapped / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
18574 pDevice->pulse.mappedBufferFramesRemainingCapture = pDevice->pulse.mappedBufferFramesCapacityCapture;
18575
18576 #if defined(MA_DEBUG_OUTPUT)
18577 printf("[PulseAudio] ma_device_read__pulse: Mapped. mappedBufferFramesCapacityCapture=%d, mappedBufferFramesRemainingCapture=%d\n", pDevice->pulse.mappedBufferFramesCapacityCapture, pDevice->pulse.mappedBufferFramesRemainingCapture);
18578 #endif
18579
18580 if (pDevice->pulse.pMappedBufferCapture == NULL) {
18581 /* It's a hole. */
18582 #if defined(MA_DEBUG_OUTPUT)
18583 printf("[PulseAudio] ma_device_read__pulse: Call pa_stream_peek(). Hole.\n");
18584 #endif
18585 }
18586
18587 break;
18588 } else {
18589 if (pDevice->pulse.pMappedBufferCapture == NULL) {
18590 /* Nothing available yet. Need to wait for more. */
18591
18592 /*
18593 I have had reports of a deadlock in this part of the code. I have reproduced this when using the "Built-in Audio Analogue Stereo" device without
18594 an actual microphone connected. I'm experimenting here by not blocking in pa_mainloop_iterate() and instead sleep for a bit when there are no
18595 dispatches.
18596 */
18597 error = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 0, NULL);
18598 if (error < 0) {
18599 return ma_result_from_pulse(error);
18600 }
18601
18602 /* Sleep for a bit if nothing was dispatched. */
18603 if (error == 0) {
18604 ma_sleep(1);
18605 }
18606
18607 #if defined(MA_DEBUG_OUTPUT)
18608 printf("[PulseAudio] ma_device_read__pulse: No data available. Waiting. mappedBufferFramesCapacityCapture=%d, mappedBufferFramesRemainingCapture=%d\n", pDevice->pulse.mappedBufferFramesCapacityCapture, pDevice->pulse.mappedBufferFramesRemainingCapture);
18609 #endif
18610 } else {
18611 /* Getting here means we mapped 0 bytes, but have a non-NULL buffer. I don't think this should ever happen. */
18612 MA_ASSERT(MA_FALSE);
18613 }
18614 }
18615 }
18616 }
18617
18618 if (pFramesRead != NULL) {
18619 *pFramesRead = totalFramesRead;
18620 }
18621
18622 return MA_SUCCESS;
18623}
18624
18625static ma_result ma_device_main_loop__pulse(ma_device* pDevice)
18626{
18627 ma_result result = MA_SUCCESS;
18628 ma_bool32 exitLoop = MA_FALSE;
18629
18630 MA_ASSERT(pDevice != NULL);
18631
18632 /* The stream needs to be uncorked first. We do this at the top for both capture and playback for PulseAudio. */
18633 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
18634 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0);
18635 if (result != MA_SUCCESS) {
18636 return result;
18637 }
18638 }
18639 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
18640 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0);
18641 if (result != MA_SUCCESS) {
18642 return result;
18643 }
18644 }
18645
18646
18647 while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
18648 switch (pDevice->type)
18649 {
18650 case ma_device_type_duplex:
18651 {
18652 /* The process is: device_read -> convert -> callback -> convert -> device_write */
18653 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
18654 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
18655
18656 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
18657 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18658 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18659 ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
18660 ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
18661 ma_uint32 capturedDeviceFramesRemaining;
18662 ma_uint32 capturedDeviceFramesProcessed;
18663 ma_uint32 capturedDeviceFramesToProcess;
18664 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
18665 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
18666 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
18667 }
18668
18669 result = ma_device_read__pulse(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
18670 if (result != MA_SUCCESS) {
18671 exitLoop = MA_TRUE;
18672 break;
18673 }
18674
18675 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
18676 capturedDeviceFramesProcessed = 0;
18677
18678 for (;;) {
18679 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18680 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18681 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
18682 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
18683 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
18684 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
18685 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
18686
18687 /* Convert capture data from device format to client format. */
18688 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
18689 if (result != MA_SUCCESS) {
18690 break;
18691 }
18692
18693 /*
18694 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
18695 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
18696 */
18697 if (capturedClientFramesToProcessThisIteration == 0) {
18698 break;
18699 }
18700
18701 ma_device__on_data(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
18702
18703 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
18704 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
18705
18706 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
18707 for (;;) {
18708 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
18709 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
18710 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
18711 if (result != MA_SUCCESS) {
18712 break;
18713 }
18714
18715 result = ma_device_write__pulse(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
18716 if (result != MA_SUCCESS) {
18717 exitLoop = MA_TRUE;
18718 break;
18719 }
18720
18721 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
18722 if (capturedClientFramesToProcessThisIteration == 0) {
18723 break;
18724 }
18725 }
18726
18727 /* In case an error happened from ma_device_write__pulse()... */
18728 if (result != MA_SUCCESS) {
18729 exitLoop = MA_TRUE;
18730 break;
18731 }
18732 }
18733
18734 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
18735 }
18736 } break;
18737
18738 case ma_device_type_capture:
18739 {
18740 ma_uint8 intermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18741 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
18742 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
18743 ma_uint32 framesReadThisPeriod = 0;
18744 while (framesReadThisPeriod < periodSizeInFrames) {
18745 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
18746 ma_uint32 framesProcessed;
18747 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
18748 if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
18749 framesToReadThisIteration = intermediaryBufferSizeInFrames;
18750 }
18751
18752 result = ma_device_read__pulse(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
18753 if (result != MA_SUCCESS) {
18754 exitLoop = MA_TRUE;
18755 break;
18756 }
18757
18758 ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
18759
18760 framesReadThisPeriod += framesProcessed;
18761 }
18762 } break;
18763
18764 case ma_device_type_playback:
18765 {
18766 ma_uint8 intermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18767 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
18768 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
18769 ma_uint32 framesWrittenThisPeriod = 0;
18770 while (framesWrittenThisPeriod < periodSizeInFrames) {
18771 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
18772 ma_uint32 framesProcessed;
18773 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
18774 if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
18775 framesToWriteThisIteration = intermediaryBufferSizeInFrames;
18776 }
18777
18778 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
18779
18780 result = ma_device_write__pulse(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
18781 if (result != MA_SUCCESS) {
18782 exitLoop = MA_TRUE;
18783 break;
18784 }
18785
18786 framesWrittenThisPeriod += framesProcessed;
18787 }
18788 } break;
18789
18790 /* To silence a warning. Will never hit this. */
18791 case ma_device_type_loopback:
18792 default: break;
18793 }
18794 }
18795
18796 /* Here is where the device needs to be stopped. */
18797 ma_device_stop__pulse(pDevice);
18798
18799 return result;
18800}
18801
18802
18803static ma_result ma_context_uninit__pulse(ma_context* pContext)
18804{
18805 MA_ASSERT(pContext != NULL);
18806 MA_ASSERT(pContext->backend == ma_backend_pulseaudio);
18807
18808 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
18809 pContext->pulse.pServerName = NULL;
18810
18811 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
18812 pContext->pulse.pApplicationName = NULL;
18813
18814#ifndef MA_NO_RUNTIME_LINKING
18815 ma_dlclose(pContext, pContext->pulse.pulseSO);
18816#endif
18817
18818 return MA_SUCCESS;
18819}
18820
18821static ma_result ma_context_init__pulse(const ma_context_config* pConfig, ma_context* pContext)
18822{
18823#ifndef MA_NO_RUNTIME_LINKING
18824 const char* libpulseNames[] = {
18825 "libpulse.so",
18826 "libpulse.so.0"
18827 };
18828 size_t i;
18829
18830 for (i = 0; i < ma_countof(libpulseNames); ++i) {
18831 pContext->pulse.pulseSO = ma_dlopen(pContext, libpulseNames[i]);
18832 if (pContext->pulse.pulseSO != NULL) {
18833 break;
18834 }
18835 }
18836
18837 if (pContext->pulse.pulseSO == NULL) {
18838 return MA_NO_BACKEND;
18839 }
18840
18841 pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_new");
18842 pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_free");
18843 pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_get_api");
18844 pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_iterate");
18845 pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_wakeup");
18846 pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_new");
18847 pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_unref");
18848 pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_connect");
18849 pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_disconnect");
18850 pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_set_state_callback");
18851 pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_state");
18852 pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_list");
18853 pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_list");
18854 pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name");
18855 pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_by_name");
18856 pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_unref");
18857 pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_get_state");
18858 pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_init_extend");
18859 pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_valid");
18860 pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_compatible");
18861 pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_new");
18862 pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_unref");
18863 pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_playback");
18864 pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_record");
18865 pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_disconnect");
18866 pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_state");
18867 pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_sample_spec");
18868 pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_channel_map");
18869 pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_buffer_attr");
18870 pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_buffer_attr");
18871 pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_device_name");
18872 pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback");
18873 pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback");
18874 pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush");
18875 pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain");
18876 pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_corked");
18877 pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_cork");
18878 pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_trigger");
18879 pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_begin_write");
18880 pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_write");
18881 pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_peek");
18882 pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drop");
18883 pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_writable_size");
18884 pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_readable_size");
18885#else
18886 /* This strange assignment system is just for type safety. */
18887 ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new;
18888 ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free;
18889 ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api;
18890 ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate;
18891 ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup;
18892 ma_pa_context_new_proc _pa_context_new = pa_context_new;
18893 ma_pa_context_unref_proc _pa_context_unref = pa_context_unref;
18894 ma_pa_context_connect_proc _pa_context_connect = pa_context_connect;
18895 ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect;
18896 ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback;
18897 ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state;
18898 ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list;
18899 ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list;
18900 ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name;
18901 ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name;
18902 ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref;
18903 ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state;
18904 ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend;
18905 ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid;
18906 ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible;
18907 ma_pa_stream_new_proc _pa_stream_new = pa_stream_new;
18908 ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref;
18909 ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback;
18910 ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record;
18911 ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect;
18912 ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state;
18913 ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec;
18914 ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map;
18915 ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr;
18916 ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr;
18917 ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name;
18918 ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback;
18919 ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback;
18920 ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush;
18921 ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain;
18922 ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked;
18923 ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork;
18924 ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger;
18925 ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write;
18926 ma_pa_stream_write_proc _pa_stream_write = pa_stream_write;
18927 ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek;
18928 ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop;
18929 ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size;
18930 ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size;
18931
18932 pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new;
18933 pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free;
18934 pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api;
18935 pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate;
18936 pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup;
18937 pContext->pulse.pa_context_new = (ma_proc)_pa_context_new;
18938 pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref;
18939 pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect;
18940 pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect;
18941 pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback;
18942 pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state;
18943 pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list;
18944 pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list;
18945 pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name;
18946 pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name;
18947 pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref;
18948 pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state;
18949 pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend;
18950 pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid;
18951 pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible;
18952 pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new;
18953 pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref;
18954 pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback;
18955 pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record;
18956 pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect;
18957 pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state;
18958 pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec;
18959 pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map;
18960 pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr;
18961 pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr;
18962 pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name;
18963 pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback;
18964 pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback;
18965 pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush;
18966 pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain;
18967 pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked;
18968 pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork;
18969 pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger;
18970 pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write;
18971 pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write;
18972 pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek;
18973 pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop;
18974 pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size;
18975 pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size;
18976#endif
18977
18978 pContext->onUninit = ma_context_uninit__pulse;
18979 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__pulse;
18980 pContext->onEnumDevices = ma_context_enumerate_devices__pulse;
18981 pContext->onGetDeviceInfo = ma_context_get_device_info__pulse;
18982 pContext->onDeviceInit = ma_device_init__pulse;
18983 pContext->onDeviceUninit = ma_device_uninit__pulse;
18984 pContext->onDeviceStart = NULL;
18985 pContext->onDeviceStop = NULL;
18986 pContext->onDeviceMainLoop = ma_device_main_loop__pulse;
18987
18988 if (pConfig->pulse.pApplicationName) {
18989 pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks);
18990 }
18991 if (pConfig->pulse.pServerName) {
18992 pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks);
18993 }
18994 pContext->pulse.tryAutoSpawn = pConfig->pulse.tryAutoSpawn;
18995
18996 /*
18997 Although we have found the libpulse library, it doesn't necessarily mean PulseAudio is useable. We need to initialize
18998 and connect a dummy PulseAudio context to test PulseAudio's usability.
18999 */
19000 {
19001 ma_pa_mainloop* pMainLoop;
19002 ma_pa_mainloop_api* pAPI;
19003 ma_pa_context* pPulseContext;
19004 int error;
19005
19006 pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
19007 if (pMainLoop == NULL) {
19008 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
19009 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
19010 #ifndef MA_NO_RUNTIME_LINKING
19011 ma_dlclose(pContext, pContext->pulse.pulseSO);
19012 #endif
19013 return MA_NO_BACKEND;
19014 }
19015
19016 pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)(pMainLoop);
19017 if (pAPI == NULL) {
19018 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
19019 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
19020 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
19021 #ifndef MA_NO_RUNTIME_LINKING
19022 ma_dlclose(pContext, pContext->pulse.pulseSO);
19023 #endif
19024 return MA_NO_BACKEND;
19025 }
19026
19027 pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(pAPI, pContext->pulse.pApplicationName);
19028 if (pPulseContext == NULL) {
19029 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
19030 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
19031 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
19032 #ifndef MA_NO_RUNTIME_LINKING
19033 ma_dlclose(pContext, pContext->pulse.pulseSO);
19034 #endif
19035 return MA_NO_BACKEND;
19036 }
19037
19038 error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)(pPulseContext, pContext->pulse.pServerName, 0, NULL);
19039 if (error != MA_PA_OK) {
19040 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
19041 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
19042 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
19043 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
19044 #ifndef MA_NO_RUNTIME_LINKING
19045 ma_dlclose(pContext, pContext->pulse.pulseSO);
19046 #endif
19047 return MA_NO_BACKEND;
19048 }
19049
19050 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)(pPulseContext);
19051 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
19052 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
19053 }
19054
19055 return MA_SUCCESS;
19056}
19057#endif
19058
19059
19060/******************************************************************************
19061
19062JACK Backend
19063
19064******************************************************************************/
19065#ifdef MA_HAS_JACK
19066
19067/* It is assumed jack.h is available when compile-time linking is being used. */
19068#ifdef MA_NO_RUNTIME_LINKING
19069#include <jack/jack.h>
19070
19071typedef jack_nframes_t ma_jack_nframes_t;
19072typedef jack_options_t ma_jack_options_t;
19073typedef jack_status_t ma_jack_status_t;
19074typedef jack_client_t ma_jack_client_t;
19075typedef jack_port_t ma_jack_port_t;
19076typedef JackProcessCallback ma_JackProcessCallback;
19077typedef JackBufferSizeCallback ma_JackBufferSizeCallback;
19078typedef JackShutdownCallback ma_JackShutdownCallback;
19079#define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE
19080#define ma_JackNoStartServer JackNoStartServer
19081#define ma_JackPortIsInput JackPortIsInput
19082#define ma_JackPortIsOutput JackPortIsOutput
19083#define ma_JackPortIsPhysical JackPortIsPhysical
19084#else
19085typedef ma_uint32 ma_jack_nframes_t;
19086typedef int ma_jack_options_t;
19087typedef int ma_jack_status_t;
19088typedef struct ma_jack_client_t ma_jack_client_t;
19089typedef struct ma_jack_port_t ma_jack_port_t;
19090typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg);
19091typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg);
19092typedef void (* ma_JackShutdownCallback) (void* arg);
19093#define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio"
19094#define ma_JackNoStartServer 1
19095#define ma_JackPortIsInput 1
19096#define ma_JackPortIsOutput 2
19097#define ma_JackPortIsPhysical 4
19098#endif
19099
19100typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...);
19101typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client);
19102typedef int (* ma_jack_client_name_size_proc) ();
19103typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg);
19104typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg);
19105typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg);
19106typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client);
19107typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client);
19108typedef 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);
19109typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client);
19110typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client);
19111typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port);
19112typedef 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);
19113typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port);
19114typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes);
19115typedef void (* ma_jack_free_proc) (void* ptr);
19116
19117static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient)
19118{
19119 size_t maxClientNameSize;
19120 char clientName[256];
19121 ma_jack_status_t status;
19122 ma_jack_client_t* pClient;
19123
19124 MA_ASSERT(pContext != NULL);
19125 MA_ASSERT(ppClient != NULL);
19126
19127 if (ppClient) {
19128 *ppClient = NULL;
19129 }
19130
19131 maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */
19132 ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1);
19133
19134 pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL);
19135 if (pClient == NULL) {
19136 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
19137 }
19138
19139 if (ppClient) {
19140 *ppClient = pClient;
19141 }
19142
19143 return MA_SUCCESS;
19144}
19145
19146static ma_bool32 ma_context_is_device_id_equal__jack(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
19147{
19148 MA_ASSERT(pContext != NULL);
19149 MA_ASSERT(pID0 != NULL);
19150 MA_ASSERT(pID1 != NULL);
19151 (void)pContext;
19152
19153 return pID0->jack == pID1->jack;
19154}
19155
19156static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
19157{
19158 ma_bool32 cbResult = MA_TRUE;
19159
19160 MA_ASSERT(pContext != NULL);
19161 MA_ASSERT(callback != NULL);
19162
19163 /* Playback. */
19164 if (cbResult) {
19165 ma_device_info deviceInfo;
19166 MA_ZERO_OBJECT(&deviceInfo);
19167 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
19168 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
19169 }
19170
19171 /* Capture. */
19172 if (cbResult) {
19173 ma_device_info deviceInfo;
19174 MA_ZERO_OBJECT(&deviceInfo);
19175 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
19176 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
19177 }
19178
19179 return MA_SUCCESS;
19180}
19181
19182static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
19183{
19184 ma_jack_client_t* pClient;
19185 ma_result result;
19186 const char** ppPorts;
19187
19188 MA_ASSERT(pContext != NULL);
19189
19190 /* No exclusive mode with the JACK backend. */
19191 if (shareMode == ma_share_mode_exclusive) {
19192 return MA_SHARE_MODE_NOT_SUPPORTED;
19193 }
19194
19195 if (pDeviceID != NULL && pDeviceID->jack != 0) {
19196 return MA_NO_DEVICE; /* Don't know the device. */
19197 }
19198
19199 /* Name / Description */
19200 if (deviceType == ma_device_type_playback) {
19201 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
19202 } else {
19203 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
19204 }
19205
19206 /* Jack only supports f32 and has a specific channel count and sample rate. */
19207 pDeviceInfo->formatCount = 1;
19208 pDeviceInfo->formats[0] = ma_format_f32;
19209
19210 /* The channel count and sample rate can only be determined by opening the device. */
19211 result = ma_context_open_client__jack(pContext, &pClient);
19212 if (result != MA_SUCCESS) {
19213 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
19214 }
19215
19216 pDeviceInfo->minSampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient);
19217 pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
19218
19219 pDeviceInfo->minChannels = 0;
19220 pDeviceInfo->maxChannels = 0;
19221
19222 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));
19223 if (ppPorts == NULL) {
19224 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
19225 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
19226 }
19227
19228 while (ppPorts[pDeviceInfo->minChannels] != NULL) {
19229 pDeviceInfo->minChannels += 1;
19230 pDeviceInfo->maxChannels += 1;
19231 }
19232
19233 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
19234 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
19235
19236 (void)pContext;
19237 return MA_SUCCESS;
19238}
19239
19240
19241static void ma_device_uninit__jack(ma_device* pDevice)
19242{
19243 ma_context* pContext;
19244
19245 MA_ASSERT(pDevice != NULL);
19246
19247 pContext = pDevice->pContext;
19248 MA_ASSERT(pContext != NULL);
19249
19250 if (pDevice->jack.pClient != NULL) {
19251 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient);
19252 }
19253
19254 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
19255 ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
19256 }
19257
19258 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
19259 ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
19260 }
19261
19262 if (pDevice->type == ma_device_type_duplex) {
19263 ma_pcm_rb_uninit(&pDevice->jack.duplexRB);
19264 }
19265}
19266
19267static void ma_device__jack_shutdown_callback(void* pUserData)
19268{
19269 /* JACK died. Stop the device. */
19270 ma_device* pDevice = (ma_device*)pUserData;
19271 MA_ASSERT(pDevice != NULL);
19272
19273 ma_device_stop(pDevice);
19274}
19275
19276static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData)
19277{
19278 ma_device* pDevice = (ma_device*)pUserData;
19279 MA_ASSERT(pDevice != NULL);
19280
19281 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
19282 size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
19283 float* pNewBuffer = (float*)ma__calloc_from_callbacks(newBufferSize, &pDevice->pContext->allocationCallbacks);
19284 if (pNewBuffer == NULL) {
19285 return MA_OUT_OF_MEMORY;
19286 }
19287
19288 ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
19289
19290 pDevice->jack.pIntermediaryBufferCapture = pNewBuffer;
19291 pDevice->playback.internalPeriodSizeInFrames = frameCount;
19292 }
19293
19294 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
19295 size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
19296 float* pNewBuffer = (float*)ma__calloc_from_callbacks(newBufferSize, &pDevice->pContext->allocationCallbacks);
19297 if (pNewBuffer == NULL) {
19298 return MA_OUT_OF_MEMORY;
19299 }
19300
19301 ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
19302
19303 pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer;
19304 pDevice->playback.internalPeriodSizeInFrames = frameCount;
19305 }
19306
19307 return 0;
19308}
19309
19310static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData)
19311{
19312 ma_device* pDevice;
19313 ma_context* pContext;
19314 ma_uint32 iChannel;
19315
19316 pDevice = (ma_device*)pUserData;
19317 MA_ASSERT(pDevice != NULL);
19318
19319 pContext = pDevice->pContext;
19320 MA_ASSERT(pContext != NULL);
19321
19322 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
19323 /* Channels need to be interleaved. */
19324 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
19325 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);
19326 if (pSrc != NULL) {
19327 float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel;
19328 ma_jack_nframes_t iFrame;
19329 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
19330 *pDst = *pSrc;
19331
19332 pDst += pDevice->capture.internalChannels;
19333 pSrc += 1;
19334 }
19335 }
19336 }
19337
19338 if (pDevice->type == ma_device_type_duplex) {
19339 ma_device__handle_duplex_callback_capture(pDevice, frameCount, pDevice->jack.pIntermediaryBufferCapture, &pDevice->jack.duplexRB);
19340 } else {
19341 ma_device__send_frames_to_client(pDevice, frameCount, pDevice->jack.pIntermediaryBufferCapture);
19342 }
19343 }
19344
19345 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
19346 if (pDevice->type == ma_device_type_duplex) {
19347 ma_device__handle_duplex_callback_playback(pDevice, frameCount, pDevice->jack.pIntermediaryBufferPlayback, &pDevice->jack.duplexRB);
19348 } else {
19349 ma_device__read_frames_from_client(pDevice, frameCount, pDevice->jack.pIntermediaryBufferPlayback);
19350 }
19351
19352 /* Channels need to be deinterleaved. */
19353 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
19354 float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.pPortsPlayback[iChannel], frameCount);
19355 if (pDst != NULL) {
19356 const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel;
19357 ma_jack_nframes_t iFrame;
19358 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
19359 *pDst = *pSrc;
19360
19361 pDst += 1;
19362 pSrc += pDevice->playback.internalChannels;
19363 }
19364 }
19365 }
19366 }
19367
19368 return 0;
19369}
19370
19371static ma_result ma_device_init__jack(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
19372{
19373 ma_result result;
19374 ma_uint32 periods;
19375 ma_uint32 periodSizeInFrames;
19376
19377 MA_ASSERT(pContext != NULL);
19378 MA_ASSERT(pConfig != NULL);
19379 MA_ASSERT(pDevice != NULL);
19380
19381 if (pConfig->deviceType == ma_device_type_loopback) {
19382 return MA_DEVICE_TYPE_NOT_SUPPORTED;
19383 }
19384
19385 /* Only supporting default devices with JACK. */
19386 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID != NULL && pConfig->playback.pDeviceID->jack != 0) ||
19387 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID != NULL && pConfig->capture.pDeviceID->jack != 0)) {
19388 return MA_NO_DEVICE;
19389 }
19390
19391 /* No exclusive mode with the JACK backend. */
19392 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
19393 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
19394 return MA_SHARE_MODE_NOT_SUPPORTED;
19395 }
19396
19397 /* Open the client. */
19398 result = ma_context_open_client__jack(pContext, (ma_jack_client_t**)&pDevice->jack.pClient);
19399 if (result != MA_SUCCESS) {
19400 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
19401 }
19402
19403 /* Callbacks. */
19404 if (((ma_jack_set_process_callback_proc)pContext->jack.jack_set_process_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_process_callback, pDevice) != 0) {
19405 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
19406 }
19407 if (((ma_jack_set_buffer_size_callback_proc)pContext->jack.jack_set_buffer_size_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) {
19408 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
19409 }
19410
19411 ((ma_jack_on_shutdown_proc)pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice);
19412
19413
19414 /* The buffer size in frames can change. */
19415 periods = pConfig->periods;
19416 periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient);
19417
19418 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
19419 const char** ppPorts;
19420
19421 pDevice->capture.internalFormat = ma_format_f32;
19422 pDevice->capture.internalChannels = 0;
19423 pDevice->capture.internalSampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
19424 ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
19425
19426 ppPorts = ((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);
19427 if (ppPorts == NULL) {
19428 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
19429 }
19430
19431 while (ppPorts[pDevice->capture.internalChannels] != NULL) {
19432 char name[64];
19433 ma_strcpy_s(name, sizeof(name), "capture");
19434 ma_itoa_s((int)pDevice->capture.internalChannels, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */
19435
19436 pDevice->jack.pPortsCapture[pDevice->capture.internalChannels] = ((ma_jack_port_register_proc)pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0);
19437 if (pDevice->jack.pPortsCapture[pDevice->capture.internalChannels] == NULL) {
19438 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
19439 ma_device_uninit__jack(pDevice);
19440 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
19441 }
19442
19443 pDevice->capture.internalChannels += 1;
19444 }
19445
19446 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
19447
19448 pDevice->capture.internalPeriodSizeInFrames = periodSizeInFrames;
19449 pDevice->capture.internalPeriods = periods;
19450
19451 pDevice->jack.pIntermediaryBufferCapture = (float*)ma__calloc_from_callbacks(pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels), &pContext->allocationCallbacks);
19452 if (pDevice->jack.pIntermediaryBufferCapture == NULL) {
19453 ma_device_uninit__jack(pDevice);
19454 return MA_OUT_OF_MEMORY;
19455 }
19456 }
19457
19458 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
19459 const char** ppPorts;
19460
19461 pDevice->playback.internalFormat = ma_format_f32;
19462 pDevice->playback.internalChannels = 0;
19463 pDevice->playback.internalSampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
19464 ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
19465
19466 ppPorts = ((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);
19467 if (ppPorts == NULL) {
19468 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
19469 }
19470
19471 while (ppPorts[pDevice->playback.internalChannels] != NULL) {
19472 char name[64];
19473 ma_strcpy_s(name, sizeof(name), "playback");
19474 ma_itoa_s((int)pDevice->playback.internalChannels, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */
19475
19476 pDevice->jack.pPortsPlayback[pDevice->playback.internalChannels] = ((ma_jack_port_register_proc)pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0);
19477 if (pDevice->jack.pPortsPlayback[pDevice->playback.internalChannels] == NULL) {
19478 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
19479 ma_device_uninit__jack(pDevice);
19480 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
19481 }
19482
19483 pDevice->playback.internalChannels += 1;
19484 }
19485
19486 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
19487
19488 pDevice->playback.internalPeriodSizeInFrames = periodSizeInFrames;
19489 pDevice->playback.internalPeriods = periods;
19490
19491 pDevice->jack.pIntermediaryBufferPlayback = (float*)ma__calloc_from_callbacks(pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels), &pContext->allocationCallbacks);
19492 if (pDevice->jack.pIntermediaryBufferPlayback == NULL) {
19493 ma_device_uninit__jack(pDevice);
19494 return MA_OUT_OF_MEMORY;
19495 }
19496 }
19497
19498 if (pDevice->type == ma_device_type_duplex) {
19499 ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods);
19500 result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->pContext->allocationCallbacks, &pDevice->jack.duplexRB);
19501 if (result != MA_SUCCESS) {
19502 ma_device_uninit__jack(pDevice);
19503 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to initialize ring buffer.", result);
19504 }
19505
19506 /* We need a period to act as a buffer for cases where the playback and capture device's end up desyncing. */
19507 {
19508 ma_uint32 marginSizeInFrames = rbSizeInFrames / pDevice->capture.internalPeriods;
19509 void* pMarginData;
19510 ma_pcm_rb_acquire_write(&pDevice->jack.duplexRB, &marginSizeInFrames, &pMarginData);
19511 {
19512 MA_ZERO_MEMORY(pMarginData, marginSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
19513 }
19514 ma_pcm_rb_commit_write(&pDevice->jack.duplexRB, marginSizeInFrames, pMarginData);
19515 }
19516 }
19517
19518 return MA_SUCCESS;
19519}
19520
19521
19522static ma_result ma_device_start__jack(ma_device* pDevice)
19523{
19524 ma_context* pContext = pDevice->pContext;
19525 int resultJACK;
19526 size_t i;
19527
19528 resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient);
19529 if (resultJACK != 0) {
19530 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client.", MA_FAILED_TO_START_BACKEND_DEVICE);
19531 }
19532
19533 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
19534 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);
19535 if (ppServerPorts == NULL) {
19536 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
19537 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.", MA_ERROR);
19538 }
19539
19540 for (i = 0; ppServerPorts[i] != NULL; ++i) {
19541 const char* pServerPort = ppServerPorts[i];
19542 const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.pPortsCapture[i]);
19543
19544 resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort);
19545 if (resultJACK != 0) {
19546 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
19547 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
19548 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.", MA_ERROR);
19549 }
19550 }
19551
19552 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
19553 }
19554
19555 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
19556 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);
19557 if (ppServerPorts == NULL) {
19558 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
19559 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.", MA_ERROR);
19560 }
19561
19562 for (i = 0; ppServerPorts[i] != NULL; ++i) {
19563 const char* pServerPort = ppServerPorts[i];
19564 const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.pPortsPlayback[i]);
19565
19566 resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort);
19567 if (resultJACK != 0) {
19568 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
19569 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
19570 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.", MA_ERROR);
19571 }
19572 }
19573
19574 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
19575 }
19576
19577 return MA_SUCCESS;
19578}
19579
19580static ma_result ma_device_stop__jack(ma_device* pDevice)
19581{
19582 ma_context* pContext = pDevice->pContext;
19583 ma_stop_proc onStop;
19584
19585 if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) {
19586 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client.", MA_ERROR);
19587 }
19588
19589 onStop = pDevice->onStop;
19590 if (onStop) {
19591 onStop(pDevice);
19592 }
19593
19594 return MA_SUCCESS;
19595}
19596
19597
19598static ma_result ma_context_uninit__jack(ma_context* pContext)
19599{
19600 MA_ASSERT(pContext != NULL);
19601 MA_ASSERT(pContext->backend == ma_backend_jack);
19602
19603 ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
19604 pContext->jack.pClientName = NULL;
19605
19606#ifndef MA_NO_RUNTIME_LINKING
19607 ma_dlclose(pContext, pContext->jack.jackSO);
19608#endif
19609
19610 return MA_SUCCESS;
19611}
19612
19613static ma_result ma_context_init__jack(const ma_context_config* pConfig, ma_context* pContext)
19614{
19615#ifndef MA_NO_RUNTIME_LINKING
19616 const char* libjackNames[] = {
19617#ifdef MA_WIN32
19618 "libjack.dll"
19619#else
19620 "libjack.so",
19621 "libjack.so.0"
19622#endif
19623 };
19624 size_t i;
19625
19626 for (i = 0; i < ma_countof(libjackNames); ++i) {
19627 pContext->jack.jackSO = ma_dlopen(pContext, libjackNames[i]);
19628 if (pContext->jack.jackSO != NULL) {
19629 break;
19630 }
19631 }
19632
19633 if (pContext->jack.jackSO == NULL) {
19634 return MA_NO_BACKEND;
19635 }
19636
19637 pContext->jack.jack_client_open = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_open");
19638 pContext->jack.jack_client_close = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_close");
19639 pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_name_size");
19640 pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_process_callback");
19641 pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_buffer_size_callback");
19642 pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_on_shutdown");
19643 pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_sample_rate");
19644 pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_buffer_size");
19645 pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_ports");
19646 pContext->jack.jack_activate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_activate");
19647 pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_deactivate");
19648 pContext->jack.jack_connect = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_connect");
19649 pContext->jack.jack_port_register = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_register");
19650 pContext->jack.jack_port_name = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_name");
19651 pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_get_buffer");
19652 pContext->jack.jack_free = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_free");
19653#else
19654 /*
19655 This strange assignment system is here just to ensure type safety of miniaudio's function pointer
19656 types. If anything differs slightly the compiler should throw a warning.
19657 */
19658 ma_jack_client_open_proc _jack_client_open = jack_client_open;
19659 ma_jack_client_close_proc _jack_client_close = jack_client_close;
19660 ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size;
19661 ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback;
19662 ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback;
19663 ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown;
19664 ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate;
19665 ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size;
19666 ma_jack_get_ports_proc _jack_get_ports = jack_get_ports;
19667 ma_jack_activate_proc _jack_activate = jack_activate;
19668 ma_jack_deactivate_proc _jack_deactivate = jack_deactivate;
19669 ma_jack_connect_proc _jack_connect = jack_connect;
19670 ma_jack_port_register_proc _jack_port_register = jack_port_register;
19671 ma_jack_port_name_proc _jack_port_name = jack_port_name;
19672 ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer;
19673 ma_jack_free_proc _jack_free = jack_free;
19674
19675 pContext->jack.jack_client_open = (ma_proc)_jack_client_open;
19676 pContext->jack.jack_client_close = (ma_proc)_jack_client_close;
19677 pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size;
19678 pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback;
19679 pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback;
19680 pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown;
19681 pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate;
19682 pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size;
19683 pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports;
19684 pContext->jack.jack_activate = (ma_proc)_jack_activate;
19685 pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate;
19686 pContext->jack.jack_connect = (ma_proc)_jack_connect;
19687 pContext->jack.jack_port_register = (ma_proc)_jack_port_register;
19688 pContext->jack.jack_port_name = (ma_proc)_jack_port_name;
19689 pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer;
19690 pContext->jack.jack_free = (ma_proc)_jack_free;
19691#endif
19692
19693 pContext->isBackendAsynchronous = MA_TRUE;
19694
19695 pContext->onUninit = ma_context_uninit__jack;
19696 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__jack;
19697 pContext->onEnumDevices = ma_context_enumerate_devices__jack;
19698 pContext->onGetDeviceInfo = ma_context_get_device_info__jack;
19699 pContext->onDeviceInit = ma_device_init__jack;
19700 pContext->onDeviceUninit = ma_device_uninit__jack;
19701 pContext->onDeviceStart = ma_device_start__jack;
19702 pContext->onDeviceStop = ma_device_stop__jack;
19703
19704 if (pConfig->jack.pClientName != NULL) {
19705 pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks);
19706 }
19707 pContext->jack.tryStartServer = pConfig->jack.tryStartServer;
19708
19709 /*
19710 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
19711 a temporary client.
19712 */
19713 {
19714 ma_jack_client_t* pDummyClient;
19715 ma_result result = ma_context_open_client__jack(pContext, &pDummyClient);
19716 if (result != MA_SUCCESS) {
19717 ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
19718 #ifndef MA_NO_RUNTIME_LINKING
19719 ma_dlclose(pContext, pContext->jack.jackSO);
19720 #endif
19721 return MA_NO_BACKEND;
19722 }
19723
19724 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient);
19725 }
19726
19727 return MA_SUCCESS;
19728}
19729#endif /* JACK */
19730
19731
19732
19733/******************************************************************************
19734
19735Core Audio Backend
19736
19737******************************************************************************/
19738#ifdef MA_HAS_COREAUDIO
19739#include <TargetConditionals.h>
19740
19741#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1
19742 #define MA_APPLE_MOBILE
19743 #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
19744 #define MA_APPLE_TV
19745 #endif
19746 #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
19747 #define MA_APPLE_WATCH
19748 #endif
19749#else
19750 #define MA_APPLE_DESKTOP
19751#endif
19752
19753#if defined(MA_APPLE_DESKTOP)
19754#include <CoreAudio/CoreAudio.h>
19755#else
19756#include <AVFoundation/AVFoundation.h>
19757#endif
19758
19759#include <AudioToolbox/AudioToolbox.h>
19760
19761/* CoreFoundation */
19762typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding);
19763typedef void (* ma_CFRelease_proc)(CFTypeRef cf);
19764
19765/* CoreAudio */
19766#if defined(MA_APPLE_DESKTOP)
19767typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData);
19768typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize);
19769typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
19770typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
19771typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
19772#endif
19773
19774/* AudioToolbox */
19775typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc);
19776typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance);
19777typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance);
19778typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit);
19779typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit);
19780typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData);
19781typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable);
19782typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize);
19783typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize);
19784typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit);
19785typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);
19786
19787
19788#define MA_COREAUDIO_OUTPUT_BUS 0
19789#define MA_COREAUDIO_INPUT_BUS 1
19790
19791static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit);
19792
19793/*
19794Core Audio
19795
19796So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation
19797apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose
19798needing to figure out how this darn thing works, I'm going to outline a few things here.
19799
19800Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be
19801able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen
19802that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent
19803and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the
19804distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API.
19805
19806Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When
19807retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific
19808data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the
19809devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be
19810the central APIs for retrieving information about the system and specific devices.
19811
19812To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a
19813structure with three variables and is used to identify which property you are getting or setting. The first is the "selector"
19814which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is
19815typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and
19816kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to
19817kAudioObjectPropertyElementMaster in miniaudio's case. I don't know of any cases where this would be set to anything different.
19818
19819Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size
19820of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property
19821address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the
19822size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of
19823AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count.
19824*/
19825
19826static ma_result ma_result_from_OSStatus(OSStatus status)
19827{
19828 switch (status)
19829 {
19830 case noErr: return MA_SUCCESS;
19831 #if defined(MA_APPLE_DESKTOP)
19832 case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED;
19833 case kAudioHardwareUnspecifiedError: return MA_ERROR;
19834 case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS;
19835 case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION;
19836 case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION;
19837 case kAudioHardwareBadObjectError: return MA_INVALID_ARGS;
19838 case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS;
19839 case kAudioHardwareBadStreamError: return MA_INVALID_ARGS;
19840 case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION;
19841 case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED;
19842 case kAudioDevicePermissionsError: return MA_ACCESS_DENIED;
19843 #endif
19844 default: return MA_ERROR;
19845 }
19846}
19847
19848#if 0
19849static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit)
19850{
19851 switch (bit)
19852 {
19853 case kAudioChannelBit_Left: return MA_CHANNEL_LEFT;
19854 case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT;
19855 case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER;
19856 case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE;
19857 case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT;
19858 case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT;
19859 case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
19860 case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
19861 case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER;
19862 case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
19863 case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
19864 case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
19865 case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
19866 case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
19867 case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
19868 case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
19869 case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
19870 case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
19871 default: return MA_CHANNEL_NONE;
19872 }
19873}
19874#endif
19875
19876static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label)
19877{
19878 switch (label)
19879 {
19880 case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE;
19881 case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE;
19882 case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE;
19883 case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT;
19884 case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT;
19885 case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER;
19886 case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE;
19887 case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT;
19888 case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT;
19889 case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
19890 case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
19891 case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER;
19892 case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
19893 case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
19894 case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
19895 case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
19896 case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
19897 case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
19898 case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
19899 case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
19900 case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
19901 case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT;
19902 case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT;
19903 case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT;
19904 case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT;
19905 case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE;
19906 case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT;
19907 case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT;
19908 case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE;
19909 case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO;
19910 case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO;
19911 case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO;
19912 case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER;
19913 case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE;
19914 case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE;
19915 case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE;
19916 case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE;
19917 case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE;
19918 case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT;
19919 case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT;
19920 case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT;
19921 case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT;
19922 case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT;
19923 case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT;
19924 case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE;
19925 case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE;
19926 case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE;
19927 case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0;
19928 case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1;
19929 case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2;
19930 case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3;
19931 case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4;
19932 case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5;
19933 case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6;
19934 case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7;
19935 case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8;
19936 case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9;
19937 case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10;
19938 case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11;
19939 case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12;
19940 case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13;
19941 case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14;
19942 case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15;
19943 case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE;
19944
19945 #if 0 /* Introduced in a later version of macOS. */
19946 case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE;
19947 case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0;
19948 case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1;
19949 case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2;
19950 case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3;
19951 case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4;
19952 case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5;
19953 case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6;
19954 case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7;
19955 case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8;
19956 case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9;
19957 case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10;
19958 case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11;
19959 case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12;
19960 case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13;
19961 case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14;
19962 case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15;
19963 case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE;
19964 #endif
19965
19966 default: return MA_CHANNEL_NONE;
19967 }
19968}
19969
19970static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut)
19971{
19972 MA_ASSERT(pDescription != NULL);
19973 MA_ASSERT(pFormatOut != NULL);
19974
19975 *pFormatOut = ma_format_unknown; /* Safety. */
19976
19977 /* There's a few things miniaudio doesn't support. */
19978 if (pDescription->mFormatID != kAudioFormatLinearPCM) {
19979 return MA_FORMAT_NOT_SUPPORTED;
19980 }
19981
19982 /* We don't support any non-packed formats that are aligned high. */
19983 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) {
19984 return MA_FORMAT_NOT_SUPPORTED;
19985 }
19986
19987 /* Only supporting native-endian. */
19988 if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) {
19989 return MA_FORMAT_NOT_SUPPORTED;
19990 }
19991
19992 /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */
19993 /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) {
19994 return MA_FORMAT_NOT_SUPPORTED;
19995 }*/
19996
19997 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) {
19998 if (pDescription->mBitsPerChannel == 32) {
19999 *pFormatOut = ma_format_f32;
20000 return MA_SUCCESS;
20001 }
20002 } else {
20003 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) {
20004 if (pDescription->mBitsPerChannel == 16) {
20005 *pFormatOut = ma_format_s16;
20006 return MA_SUCCESS;
20007 } else if (pDescription->mBitsPerChannel == 24) {
20008 if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) {
20009 *pFormatOut = ma_format_s24;
20010 return MA_SUCCESS;
20011 } else {
20012 if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) {
20013 /* TODO: Implement ma_format_s24_32. */
20014 /**pFormatOut = ma_format_s24_32;*/
20015 /*return MA_SUCCESS;*/
20016 return MA_FORMAT_NOT_SUPPORTED;
20017 }
20018 }
20019 } else if (pDescription->mBitsPerChannel == 32) {
20020 *pFormatOut = ma_format_s32;
20021 return MA_SUCCESS;
20022 }
20023 } else {
20024 if (pDescription->mBitsPerChannel == 8) {
20025 *pFormatOut = ma_format_u8;
20026 return MA_SUCCESS;
20027 }
20028 }
20029 }
20030
20031 /* Getting here means the format is not supported. */
20032 return MA_FORMAT_NOT_SUPPORTED;
20033}
20034
20035static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel channelMap[MA_MAX_CHANNELS])
20036{
20037 MA_ASSERT(pChannelLayout != NULL);
20038
20039 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
20040 UInt32 iChannel;
20041 for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions; ++iChannel) {
20042 channelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel);
20043 }
20044 } else
20045#if 0
20046 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
20047 /* This is the same kind of system that's used by Windows audio APIs. */
20048 UInt32 iChannel = 0;
20049 UInt32 iBit;
20050 AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap;
20051 for (iBit = 0; iBit < 32; ++iBit) {
20052 AudioChannelBitmap bit = bitmap & (1 << iBit);
20053 if (bit != 0) {
20054 channelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit);
20055 }
20056 }
20057 } else
20058#endif
20059 {
20060 /*
20061 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
20062 be updated to determine the mapping based on the tag.
20063 */
20064 UInt32 channelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);
20065 switch (pChannelLayout->mChannelLayoutTag)
20066 {
20067 case kAudioChannelLayoutTag_Mono:
20068 case kAudioChannelLayoutTag_Stereo:
20069 case kAudioChannelLayoutTag_StereoHeadphones:
20070 case kAudioChannelLayoutTag_MatrixStereo:
20071 case kAudioChannelLayoutTag_MidSide:
20072 case kAudioChannelLayoutTag_XY:
20073 case kAudioChannelLayoutTag_Binaural:
20074 case kAudioChannelLayoutTag_Ambisonic_B_Format:
20075 {
20076 ma_get_standard_channel_map(ma_standard_channel_map_default, channelCount, channelMap);
20077 } break;
20078
20079 case kAudioChannelLayoutTag_Octagonal:
20080 {
20081 channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
20082 channelMap[6] = MA_CHANNEL_SIDE_LEFT;
20083 } /* Intentional fallthrough. */
20084 case kAudioChannelLayoutTag_Hexagonal:
20085 {
20086 channelMap[5] = MA_CHANNEL_BACK_CENTER;
20087 } /* Intentional fallthrough. */
20088 case kAudioChannelLayoutTag_Pentagonal:
20089 {
20090 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
20091 } /* Intentional fallghrough. */
20092 case kAudioChannelLayoutTag_Quadraphonic:
20093 {
20094 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
20095 channelMap[2] = MA_CHANNEL_BACK_LEFT;
20096 channelMap[1] = MA_CHANNEL_RIGHT;
20097 channelMap[0] = MA_CHANNEL_LEFT;
20098 } break;
20099
20100 /* TODO: Add support for more tags here. */
20101
20102 default:
20103 {
20104 ma_get_standard_channel_map(ma_standard_channel_map_default, channelCount, channelMap);
20105 } break;
20106 }
20107 }
20108
20109 return MA_SUCCESS;
20110}
20111
20112
20113#if defined(MA_APPLE_DESKTOP)
20114static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */
20115{
20116 AudioObjectPropertyAddress propAddressDevices;
20117 UInt32 deviceObjectsDataSize;
20118 OSStatus status;
20119 AudioObjectID* pDeviceObjectIDs;
20120
20121 MA_ASSERT(pContext != NULL);
20122 MA_ASSERT(pDeviceCount != NULL);
20123 MA_ASSERT(ppDeviceObjectIDs != NULL);
20124
20125 /* Safety. */
20126 *pDeviceCount = 0;
20127 *ppDeviceObjectIDs = NULL;
20128
20129 propAddressDevices.mSelector = kAudioHardwarePropertyDevices;
20130 propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal;
20131 propAddressDevices.mElement = kAudioObjectPropertyElementMaster;
20132
20133 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize);
20134 if (status != noErr) {
20135 return ma_result_from_OSStatus(status);
20136 }
20137
20138 pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks);
20139 if (pDeviceObjectIDs == NULL) {
20140 return MA_OUT_OF_MEMORY;
20141 }
20142
20143 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs);
20144 if (status != noErr) {
20145 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
20146 return ma_result_from_OSStatus(status);
20147 }
20148
20149 *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID);
20150 *ppDeviceObjectIDs = pDeviceObjectIDs;
20151
20152 return MA_SUCCESS;
20153}
20154
20155static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID)
20156{
20157 AudioObjectPropertyAddress propAddress;
20158 UInt32 dataSize;
20159 OSStatus status;
20160
20161 MA_ASSERT(pContext != NULL);
20162
20163 propAddress.mSelector = kAudioDevicePropertyDeviceUID;
20164 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
20165 propAddress.mElement = kAudioObjectPropertyElementMaster;
20166
20167 dataSize = sizeof(*pUID);
20168 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID);
20169 if (status != noErr) {
20170 return ma_result_from_OSStatus(status);
20171 }
20172
20173 return MA_SUCCESS;
20174}
20175
20176static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
20177{
20178 CFStringRef uid;
20179 ma_result result;
20180
20181 MA_ASSERT(pContext != NULL);
20182
20183 result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid);
20184 if (result != MA_SUCCESS) {
20185 return result;
20186 }
20187
20188 if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
20189 return MA_ERROR;
20190 }
20191
20192 ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid);
20193 return MA_SUCCESS;
20194}
20195
20196static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
20197{
20198 AudioObjectPropertyAddress propAddress;
20199 CFStringRef deviceName = NULL;
20200 UInt32 dataSize;
20201 OSStatus status;
20202
20203 MA_ASSERT(pContext != NULL);
20204
20205 propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
20206 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
20207 propAddress.mElement = kAudioObjectPropertyElementMaster;
20208
20209 dataSize = sizeof(deviceName);
20210 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName);
20211 if (status != noErr) {
20212 return ma_result_from_OSStatus(status);
20213 }
20214
20215 if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
20216 return MA_ERROR;
20217 }
20218
20219 ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName);
20220 return MA_SUCCESS;
20221}
20222
20223static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope)
20224{
20225 AudioObjectPropertyAddress propAddress;
20226 UInt32 dataSize;
20227 OSStatus status;
20228 AudioBufferList* pBufferList;
20229 ma_bool32 isSupported;
20230
20231 MA_ASSERT(pContext != NULL);
20232
20233 /* 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. */
20234 propAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
20235 propAddress.mScope = scope;
20236 propAddress.mElement = kAudioObjectPropertyElementMaster;
20237
20238 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
20239 if (status != noErr) {
20240 return MA_FALSE;
20241 }
20242
20243 pBufferList = (AudioBufferList*)ma__malloc_from_callbacks(dataSize, &pContext->allocationCallbacks);
20244 if (pBufferList == NULL) {
20245 return MA_FALSE; /* Out of memory. */
20246 }
20247
20248 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList);
20249 if (status != noErr) {
20250 ma__free_from_callbacks(pBufferList, &pContext->allocationCallbacks);
20251 return MA_FALSE;
20252 }
20253
20254 isSupported = MA_FALSE;
20255 if (pBufferList->mNumberBuffers > 0) {
20256 isSupported = MA_TRUE;
20257 }
20258
20259 ma__free_from_callbacks(pBufferList, &pContext->allocationCallbacks);
20260 return isSupported;
20261}
20262
20263static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID)
20264{
20265 return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput);
20266}
20267
20268static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID)
20269{
20270 return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput);
20271}
20272
20273
20274static 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(). */
20275{
20276 AudioObjectPropertyAddress propAddress;
20277 UInt32 dataSize;
20278 OSStatus status;
20279 AudioStreamRangedDescription* pDescriptions;
20280
20281 MA_ASSERT(pContext != NULL);
20282 MA_ASSERT(pDescriptionCount != NULL);
20283 MA_ASSERT(ppDescriptions != NULL);
20284
20285 /*
20286 TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My
20287 MacBook Pro uses s24/32 format, however, which miniaudio does not currently support.
20288 */
20289 propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/
20290 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
20291 propAddress.mElement = kAudioObjectPropertyElementMaster;
20292
20293 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
20294 if (status != noErr) {
20295 return ma_result_from_OSStatus(status);
20296 }
20297
20298 pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks);
20299 if (pDescriptions == NULL) {
20300 return MA_OUT_OF_MEMORY;
20301 }
20302
20303 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions);
20304 if (status != noErr) {
20305 ma_free(pDescriptions, &pContext->allocationCallbacks);
20306 return ma_result_from_OSStatus(status);
20307 }
20308
20309 *pDescriptionCount = dataSize / sizeof(*pDescriptions);
20310 *ppDescriptions = pDescriptions;
20311 return MA_SUCCESS;
20312}
20313
20314
20315static 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(). */
20316{
20317 AudioObjectPropertyAddress propAddress;
20318 UInt32 dataSize;
20319 OSStatus status;
20320 AudioChannelLayout* pChannelLayout;
20321
20322 MA_ASSERT(pContext != NULL);
20323 MA_ASSERT(ppChannelLayout != NULL);
20324
20325 *ppChannelLayout = NULL; /* Safety. */
20326
20327 propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
20328 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
20329 propAddress.mElement = kAudioObjectPropertyElementMaster;
20330
20331 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
20332 if (status != noErr) {
20333 return ma_result_from_OSStatus(status);
20334 }
20335
20336 pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks);
20337 if (pChannelLayout == NULL) {
20338 return MA_OUT_OF_MEMORY;
20339 }
20340
20341 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout);
20342 if (status != noErr) {
20343 ma_free(pChannelLayout, &pContext->allocationCallbacks);
20344 return ma_result_from_OSStatus(status);
20345 }
20346
20347 *ppChannelLayout = pChannelLayout;
20348 return MA_SUCCESS;
20349}
20350
20351static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount)
20352{
20353 AudioChannelLayout* pChannelLayout;
20354 ma_result result;
20355
20356 MA_ASSERT(pContext != NULL);
20357 MA_ASSERT(pChannelCount != NULL);
20358
20359 *pChannelCount = 0; /* Safety. */
20360
20361 result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
20362 if (result != MA_SUCCESS) {
20363 return result;
20364 }
20365
20366 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
20367 *pChannelCount = pChannelLayout->mNumberChannelDescriptions;
20368 } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
20369 *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap);
20370 } else {
20371 *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);
20372 }
20373
20374 ma_free(pChannelLayout, &pContext->allocationCallbacks);
20375 return MA_SUCCESS;
20376}
20377
20378#if 0
20379static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel channelMap[MA_MAX_CHANNELS])
20380{
20381 AudioChannelLayout* pChannelLayout;
20382 ma_result result;
20383
20384 MA_ASSERT(pContext != NULL);
20385
20386 result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
20387 if (result != MA_SUCCESS) {
20388 return result; /* Rather than always failing here, would it be more robust to simply assume a default? */
20389 }
20390
20391 result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, channelMap);
20392 if (result != MA_SUCCESS) {
20393 ma_free(pChannelLayout, &pContext->allocationCallbacks);
20394 return result;
20395 }
20396
20397 ma_free(pChannelLayout, &pContext->allocationCallbacks);
20398 return result;
20399}
20400#endif
20401
20402static 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(). */
20403{
20404 AudioObjectPropertyAddress propAddress;
20405 UInt32 dataSize;
20406 OSStatus status;
20407 AudioValueRange* pSampleRateRanges;
20408
20409 MA_ASSERT(pContext != NULL);
20410 MA_ASSERT(pSampleRateRangesCount != NULL);
20411 MA_ASSERT(ppSampleRateRanges != NULL);
20412
20413 /* Safety. */
20414 *pSampleRateRangesCount = 0;
20415 *ppSampleRateRanges = NULL;
20416
20417 propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
20418 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
20419 propAddress.mElement = kAudioObjectPropertyElementMaster;
20420
20421 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
20422 if (status != noErr) {
20423 return ma_result_from_OSStatus(status);
20424 }
20425
20426 pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks);
20427 if (pSampleRateRanges == NULL) {
20428 return MA_OUT_OF_MEMORY;
20429 }
20430
20431 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges);
20432 if (status != noErr) {
20433 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
20434 return ma_result_from_OSStatus(status);
20435 }
20436
20437 *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges);
20438 *ppSampleRateRanges = pSampleRateRanges;
20439 return MA_SUCCESS;
20440}
20441
20442#if 0
20443static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut)
20444{
20445 UInt32 sampleRateRangeCount;
20446 AudioValueRange* pSampleRateRanges;
20447 ma_result result;
20448
20449 MA_ASSERT(pContext != NULL);
20450 MA_ASSERT(pSampleRateOut != NULL);
20451
20452 *pSampleRateOut = 0; /* Safety. */
20453
20454 result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
20455 if (result != MA_SUCCESS) {
20456 return result;
20457 }
20458
20459 if (sampleRateRangeCount == 0) {
20460 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
20461 return MA_ERROR; /* Should never hit this case should we? */
20462 }
20463
20464 if (sampleRateIn == 0) {
20465 /* Search in order of miniaudio's preferred priority. */
20466 UInt32 iMALSampleRate;
20467 for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) {
20468 ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate];
20469 UInt32 iCASampleRate;
20470 for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) {
20471 AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate];
20472 if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) {
20473 *pSampleRateOut = malSampleRate;
20474 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
20475 return MA_SUCCESS;
20476 }
20477 }
20478 }
20479
20480 /*
20481 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
20482 case we just fall back to the first one reported by Core Audio.
20483 */
20484 MA_ASSERT(sampleRateRangeCount > 0);
20485
20486 *pSampleRateOut = pSampleRateRanges[0].mMinimum;
20487 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
20488 return MA_SUCCESS;
20489 } else {
20490 /* Find the closest match to this sample rate. */
20491 UInt32 currentAbsoluteDifference = INT32_MAX;
20492 UInt32 iCurrentClosestRange = (UInt32)-1;
20493 UInt32 iRange;
20494 for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) {
20495 if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) {
20496 *pSampleRateOut = sampleRateIn;
20497 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
20498 return MA_SUCCESS;
20499 } else {
20500 UInt32 absoluteDifference;
20501 if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) {
20502 absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn;
20503 } else {
20504 absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum;
20505 }
20506
20507 if (currentAbsoluteDifference > absoluteDifference) {
20508 currentAbsoluteDifference = absoluteDifference;
20509 iCurrentClosestRange = iRange;
20510 }
20511 }
20512 }
20513
20514 MA_ASSERT(iCurrentClosestRange != (UInt32)-1);
20515
20516 *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum;
20517 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
20518 return MA_SUCCESS;
20519 }
20520
20521 /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */
20522 /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/
20523 /*return MA_ERROR;*/
20524}
20525#endif
20526
20527static 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)
20528{
20529 AudioObjectPropertyAddress propAddress;
20530 AudioValueRange bufferSizeRange;
20531 UInt32 dataSize;
20532 OSStatus status;
20533
20534 MA_ASSERT(pContext != NULL);
20535 MA_ASSERT(pBufferSizeInFramesOut != NULL);
20536
20537 *pBufferSizeInFramesOut = 0; /* Safety. */
20538
20539 propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
20540 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
20541 propAddress.mElement = kAudioObjectPropertyElementMaster;
20542
20543 dataSize = sizeof(bufferSizeRange);
20544 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange);
20545 if (status != noErr) {
20546 return ma_result_from_OSStatus(status);
20547 }
20548
20549 /* This is just a clamp. */
20550 if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) {
20551 *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum;
20552 } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) {
20553 *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum;
20554 } else {
20555 *pBufferSizeInFramesOut = bufferSizeInFramesIn;
20556 }
20557
20558 return MA_SUCCESS;
20559}
20560
20561static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut)
20562{
20563 ma_result result;
20564 ma_uint32 chosenBufferSizeInFrames;
20565 AudioObjectPropertyAddress propAddress;
20566 UInt32 dataSize;
20567 OSStatus status;
20568
20569 MA_ASSERT(pContext != NULL);
20570
20571 result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames);
20572 if (result != MA_SUCCESS) {
20573 return result;
20574 }
20575
20576 /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */
20577 propAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
20578 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
20579 propAddress.mElement = kAudioObjectPropertyElementMaster;
20580
20581 ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames);
20582
20583 /* Get the actual size of the buffer. */
20584 dataSize = sizeof(*pPeriodSizeInOut);
20585 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames);
20586 if (status != noErr) {
20587 return ma_result_from_OSStatus(status);
20588 }
20589
20590 *pPeriodSizeInOut = chosenBufferSizeInFrames;
20591 return MA_SUCCESS;
20592}
20593
20594
20595static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID)
20596{
20597 MA_ASSERT(pContext != NULL);
20598 MA_ASSERT(pDeviceObjectID != NULL);
20599
20600 /* Safety. */
20601 *pDeviceObjectID = 0;
20602
20603 if (pDeviceID == NULL) {
20604 /* Default device. */
20605 AudioObjectPropertyAddress propAddressDefaultDevice;
20606 UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
20607 AudioObjectID defaultDeviceObjectID;
20608 OSStatus status;
20609
20610 propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal;
20611 propAddressDefaultDevice.mElement = kAudioObjectPropertyElementMaster;
20612 if (deviceType == ma_device_type_playback) {
20613 propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
20614 } else {
20615 propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice;
20616 }
20617
20618 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
20619 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID);
20620 if (status == noErr) {
20621 *pDeviceObjectID = defaultDeviceObjectID;
20622 return MA_SUCCESS;
20623 }
20624 } else {
20625 /* Explicit device. */
20626 UInt32 deviceCount;
20627 AudioObjectID* pDeviceObjectIDs;
20628 ma_result result;
20629 UInt32 iDevice;
20630
20631 result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
20632 if (result != MA_SUCCESS) {
20633 return result;
20634 }
20635
20636 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
20637 AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
20638
20639 char uid[256];
20640 if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) {
20641 continue;
20642 }
20643
20644 if (deviceType == ma_device_type_playback) {
20645 if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
20646 if (strcmp(uid, pDeviceID->coreaudio) == 0) {
20647 *pDeviceObjectID = deviceObjectID;
20648 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
20649 return MA_SUCCESS;
20650 }
20651 }
20652 } else {
20653 if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
20654 if (strcmp(uid, pDeviceID->coreaudio) == 0) {
20655 *pDeviceObjectID = deviceObjectID;
20656 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
20657 return MA_SUCCESS;
20658 }
20659 }
20660 }
20661 }
20662
20663 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
20664 }
20665
20666 /* If we get here it means we couldn't find the device. */
20667 return MA_NO_DEVICE;
20668}
20669
20670
20671static 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, ma_bool32 usingDefaultFormat, ma_bool32 usingDefaultChannels, ma_bool32 usingDefaultSampleRate, AudioStreamBasicDescription* pFormat)
20672{
20673 UInt32 deviceFormatDescriptionCount;
20674 AudioStreamRangedDescription* pDeviceFormatDescriptions;
20675 ma_result result;
20676 ma_uint32 desiredSampleRate;
20677 ma_uint32 desiredChannelCount;
20678 ma_format desiredFormat;
20679 AudioStreamBasicDescription bestDeviceFormatSoFar;
20680 ma_bool32 hasSupportedFormat;
20681 UInt32 iFormat;
20682
20683 result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions);
20684 if (result != MA_SUCCESS) {
20685 return result;
20686 }
20687
20688 desiredSampleRate = sampleRate;
20689 if (usingDefaultSampleRate) {
20690 /*
20691 When using the device's default sample rate, we get the highest priority standard rate supported by the device. Otherwise
20692 we just use the pre-set rate.
20693 */
20694 ma_uint32 iStandardRate;
20695 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
20696 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
20697 ma_bool32 foundRate = MA_FALSE;
20698 UInt32 iDeviceRate;
20699
20700 for (iDeviceRate = 0; iDeviceRate < deviceFormatDescriptionCount; ++iDeviceRate) {
20701 ma_uint32 deviceRate = (ma_uint32)pDeviceFormatDescriptions[iDeviceRate].mFormat.mSampleRate;
20702
20703 if (deviceRate == standardRate) {
20704 desiredSampleRate = standardRate;
20705 foundRate = MA_TRUE;
20706 break;
20707 }
20708 }
20709
20710 if (foundRate) {
20711 break;
20712 }
20713 }
20714 }
20715
20716 desiredChannelCount = channels;
20717 if (usingDefaultChannels) {
20718 ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &desiredChannelCount); /* <-- Not critical if this fails. */
20719 }
20720
20721 desiredFormat = format;
20722 if (usingDefaultFormat) {
20723 desiredFormat = g_maFormatPriorities[0];
20724 }
20725
20726 /*
20727 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
20728 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.
20729 */
20730 MA_ZERO_OBJECT(&bestDeviceFormatSoFar);
20731
20732 hasSupportedFormat = MA_FALSE;
20733 for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
20734 ma_format format;
20735 ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &format);
20736 if (formatResult == MA_SUCCESS && format != ma_format_unknown) {
20737 hasSupportedFormat = MA_TRUE;
20738 bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat;
20739 break;
20740 }
20741 }
20742
20743 if (!hasSupportedFormat) {
20744 ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
20745 return MA_FORMAT_NOT_SUPPORTED;
20746 }
20747
20748
20749 for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
20750 AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat;
20751 ma_format thisSampleFormat;
20752 ma_result formatResult;
20753 ma_format bestSampleFormatSoFar;
20754
20755 /* If the format is not supported by miniaudio we need to skip this one entirely. */
20756 formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat);
20757 if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) {
20758 continue; /* The format is not supported by miniaudio. Skip. */
20759 }
20760
20761 ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar);
20762
20763 /* Getting here means the format is supported by miniaudio which makes this format a candidate. */
20764 if (thisDeviceFormat.mSampleRate != desiredSampleRate) {
20765 /*
20766 The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format
20767 so far has an equal sample rate we can just ignore this one.
20768 */
20769 if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) {
20770 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. */
20771 } else {
20772 /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */
20773 if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) {
20774 /* This format has a different sample rate _and_ a different channel count. */
20775 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
20776 continue; /* No change to the best format. */
20777 } else {
20778 /*
20779 Both this format and the best so far have different sample rates and different channel counts. Whichever has the
20780 best format is the new best.
20781 */
20782 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
20783 bestDeviceFormatSoFar = thisDeviceFormat;
20784 continue;
20785 } else {
20786 continue; /* No change to the best format. */
20787 }
20788 }
20789 } else {
20790 /* This format has a different sample rate but the desired channel count. */
20791 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
20792 /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */
20793 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
20794 bestDeviceFormatSoFar = thisDeviceFormat;
20795 continue;
20796 } else {
20797 continue; /* No change to the best format for now. */
20798 }
20799 } else {
20800 /* This format has the desired channel count, but the best so far does not. We have a new best. */
20801 bestDeviceFormatSoFar = thisDeviceFormat;
20802 continue;
20803 }
20804 }
20805 }
20806 } else {
20807 /*
20808 The sample rates match which makes this format a very high priority contender. If the best format so far has a different
20809 sample rate it needs to be replaced with this one.
20810 */
20811 if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) {
20812 bestDeviceFormatSoFar = thisDeviceFormat;
20813 continue;
20814 } else {
20815 /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */
20816 if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) {
20817 /*
20818 In this case this format has the same channel count as what the client is requesting. If the best format so far has
20819 a different count, this one becomes the new best.
20820 */
20821 if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) {
20822 bestDeviceFormatSoFar = thisDeviceFormat;
20823 continue;
20824 } else {
20825 /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */
20826 if (thisSampleFormat == desiredFormat) {
20827 bestDeviceFormatSoFar = thisDeviceFormat;
20828 break; /* Found the exact match. */
20829 } else {
20830 /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */
20831 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
20832 bestDeviceFormatSoFar = thisDeviceFormat;
20833 continue;
20834 } else {
20835 continue; /* No change to the best format for now. */
20836 }
20837 }
20838 }
20839 } else {
20840 /*
20841 In this case the channel count is different to what the client has requested. If the best so far has the same channel
20842 count as the requested count then it remains the best.
20843 */
20844 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
20845 continue;
20846 } else {
20847 /*
20848 This is the case where both have the same sample rate (good) but different channel counts. Right now both have about
20849 the same priority, but we need to compare the format now.
20850 */
20851 if (thisSampleFormat == bestSampleFormatSoFar) {
20852 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
20853 bestDeviceFormatSoFar = thisDeviceFormat;
20854 continue;
20855 } else {
20856 continue; /* No change to the best format for now. */
20857 }
20858 }
20859 }
20860 }
20861 }
20862 }
20863 }
20864
20865 *pFormat = bestDeviceFormatSoFar;
20866
20867 ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
20868 return MA_SUCCESS;
20869}
20870#endif
20871
20872static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel channelMap[MA_MAX_CHANNELS])
20873{
20874 AudioUnitScope deviceScope;
20875 AudioUnitElement deviceBus;
20876 UInt32 channelLayoutSize;
20877 OSStatus status;
20878 AudioChannelLayout* pChannelLayout;
20879 ma_result result;
20880
20881 MA_ASSERT(pContext != NULL);
20882
20883 if (deviceType == ma_device_type_playback) {
20884 deviceScope = kAudioUnitScope_Output;
20885 deviceBus = MA_COREAUDIO_OUTPUT_BUS;
20886 } else {
20887 deviceScope = kAudioUnitScope_Input;
20888 deviceBus = MA_COREAUDIO_INPUT_BUS;
20889 }
20890
20891 status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL);
20892 if (status != noErr) {
20893 return ma_result_from_OSStatus(status);
20894 }
20895
20896 pChannelLayout = (AudioChannelLayout*)ma__malloc_from_callbacks(channelLayoutSize, &pContext->allocationCallbacks);
20897 if (pChannelLayout == NULL) {
20898 return MA_OUT_OF_MEMORY;
20899 }
20900
20901 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize);
20902 if (status != noErr) {
20903 ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks);
20904 return ma_result_from_OSStatus(status);
20905 }
20906
20907 result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, channelMap);
20908 if (result != MA_SUCCESS) {
20909 ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks);
20910 return result;
20911 }
20912
20913 ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks);
20914 return MA_SUCCESS;
20915}
20916
20917static ma_bool32 ma_context_is_device_id_equal__coreaudio(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
20918{
20919 MA_ASSERT(pContext != NULL);
20920 MA_ASSERT(pID0 != NULL);
20921 MA_ASSERT(pID1 != NULL);
20922 (void)pContext;
20923
20924 return strcmp(pID0->coreaudio, pID1->coreaudio) == 0;
20925}
20926
20927static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
20928{
20929#if defined(MA_APPLE_DESKTOP)
20930 UInt32 deviceCount;
20931 AudioObjectID* pDeviceObjectIDs;
20932 ma_result result;
20933 UInt32 iDevice;
20934
20935 result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
20936 if (result != MA_SUCCESS) {
20937 return result;
20938 }
20939
20940 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
20941 AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
20942 ma_device_info info;
20943
20944 MA_ZERO_OBJECT(&info);
20945 if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) {
20946 continue;
20947 }
20948 if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) {
20949 continue;
20950 }
20951
20952 if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
20953 if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
20954 break;
20955 }
20956 }
20957 if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
20958 if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
20959 break;
20960 }
20961 }
20962 }
20963
20964 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
20965#else
20966 /* Only supporting default devices on non-Desktop platforms. */
20967 ma_device_info info;
20968
20969 MA_ZERO_OBJECT(&info);
20970 ma_strncpy_s(info.name, sizeof(info.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
20971 if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
20972 return MA_SUCCESS;
20973 }
20974
20975 MA_ZERO_OBJECT(&info);
20976 ma_strncpy_s(info.name, sizeof(info.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
20977 if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
20978 return MA_SUCCESS;
20979 }
20980#endif
20981
20982 return MA_SUCCESS;
20983}
20984
20985static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
20986{
20987 ma_result result;
20988
20989 MA_ASSERT(pContext != NULL);
20990
20991 /* No exclusive mode with the Core Audio backend for now. */
20992 if (shareMode == ma_share_mode_exclusive) {
20993 return MA_SHARE_MODE_NOT_SUPPORTED;
20994 }
20995
20996#if defined(MA_APPLE_DESKTOP)
20997 /* Desktop */
20998 {
20999 AudioObjectID deviceObjectID;
21000 UInt32 streamDescriptionCount;
21001 AudioStreamRangedDescription* pStreamDescriptions;
21002 UInt32 iStreamDescription;
21003 UInt32 sampleRateRangeCount;
21004 AudioValueRange* pSampleRateRanges;
21005
21006 result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
21007 if (result != MA_SUCCESS) {
21008 return result;
21009 }
21010
21011 result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio);
21012 if (result != MA_SUCCESS) {
21013 return result;
21014 }
21015
21016 result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name);
21017 if (result != MA_SUCCESS) {
21018 return result;
21019 }
21020
21021 /* Formats. */
21022 result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions);
21023 if (result != MA_SUCCESS) {
21024 return result;
21025 }
21026
21027 for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) {
21028 ma_format format;
21029 ma_bool32 formatExists = MA_FALSE;
21030 ma_uint32 iOutputFormat;
21031
21032 result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format);
21033 if (result != MA_SUCCESS) {
21034 continue;
21035 }
21036
21037 MA_ASSERT(format != ma_format_unknown);
21038
21039 /* Make sure the format isn't already in the output list. */
21040 for (iOutputFormat = 0; iOutputFormat < pDeviceInfo->formatCount; ++iOutputFormat) {
21041 if (pDeviceInfo->formats[iOutputFormat] == format) {
21042 formatExists = MA_TRUE;
21043 break;
21044 }
21045 }
21046
21047 if (!formatExists) {
21048 pDeviceInfo->formats[pDeviceInfo->formatCount++] = format;
21049 }
21050 }
21051
21052 ma_free(pStreamDescriptions, &pContext->allocationCallbacks);
21053
21054
21055 /* Channels. */
21056 result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &pDeviceInfo->minChannels);
21057 if (result != MA_SUCCESS) {
21058 return result;
21059 }
21060 pDeviceInfo->maxChannels = pDeviceInfo->minChannels;
21061
21062
21063 /* Sample rates. */
21064 result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
21065 if (result != MA_SUCCESS) {
21066 return result;
21067 }
21068
21069 if (sampleRateRangeCount > 0) {
21070 UInt32 iSampleRate;
21071 pDeviceInfo->minSampleRate = UINT32_MAX;
21072 pDeviceInfo->maxSampleRate = 0;
21073 for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) {
21074 if (pDeviceInfo->minSampleRate > pSampleRateRanges[iSampleRate].mMinimum) {
21075 pDeviceInfo->minSampleRate = pSampleRateRanges[iSampleRate].mMinimum;
21076 }
21077 if (pDeviceInfo->maxSampleRate < pSampleRateRanges[iSampleRate].mMaximum) {
21078 pDeviceInfo->maxSampleRate = pSampleRateRanges[iSampleRate].mMaximum;
21079 }
21080 }
21081 }
21082 }
21083#else
21084 /* Mobile */
21085 {
21086 AudioComponentDescription desc;
21087 AudioComponent component;
21088 AudioUnit audioUnit;
21089 OSStatus status;
21090 AudioUnitScope formatScope;
21091 AudioUnitElement formatElement;
21092 AudioStreamBasicDescription bestFormat;
21093 UInt32 propSize;
21094
21095 if (deviceType == ma_device_type_playback) {
21096 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
21097 } else {
21098 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
21099 }
21100
21101 /*
21102 Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is
21103 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
21104 retrieve from the AVAudioSession shared instance.
21105 */
21106 desc.componentType = kAudioUnitType_Output;
21107 desc.componentSubType = kAudioUnitSubType_RemoteIO;
21108 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
21109 desc.componentFlags = 0;
21110 desc.componentFlagsMask = 0;
21111
21112 component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
21113 if (component == NULL) {
21114 return MA_FAILED_TO_INIT_BACKEND;
21115 }
21116
21117 status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit);
21118 if (status != noErr) {
21119 return ma_result_from_OSStatus(status);
21120 }
21121
21122 formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
21123 formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
21124
21125 propSize = sizeof(bestFormat);
21126 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
21127 if (status != noErr) {
21128 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
21129 return ma_result_from_OSStatus(status);
21130 }
21131
21132 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
21133 audioUnit = NULL;
21134
21135
21136 pDeviceInfo->minChannels = bestFormat.mChannelsPerFrame;
21137 pDeviceInfo->maxChannels = bestFormat.mChannelsPerFrame;
21138
21139 pDeviceInfo->formatCount = 1;
21140 result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->formats[0]);
21141 if (result != MA_SUCCESS) {
21142 return result;
21143 }
21144
21145 /*
21146 It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do
21147 this we just get the shared instance and inspect.
21148 */
21149 @autoreleasepool {
21150 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
21151 MA_ASSERT(pAudioSession != NULL);
21152
21153 pDeviceInfo->minSampleRate = (ma_uint32)pAudioSession.sampleRate;
21154 pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
21155 }
21156 }
21157#endif
21158
21159 (void)pDeviceInfo; /* Unused. */
21160 return MA_SUCCESS;
21161}
21162
21163
21164static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList)
21165{
21166 ma_device* pDevice = (ma_device*)pUserData;
21167 ma_stream_layout layout;
21168
21169 MA_ASSERT(pDevice != NULL);
21170
21171#if defined(MA_DEBUG_OUTPUT)
21172 printf("INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pBufferList->mNumberBuffers);
21173#endif
21174
21175 /* 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. */
21176 layout = ma_stream_layout_interleaved;
21177 if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) {
21178 layout = ma_stream_layout_deinterleaved;
21179 }
21180
21181 if (layout == ma_stream_layout_interleaved) {
21182 /* For now we can assume everything is interleaved. */
21183 UInt32 iBuffer;
21184 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
21185 if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) {
21186 ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
21187 if (frameCountForThisBuffer > 0) {
21188 if (pDevice->type == ma_device_type_duplex) {
21189 ma_device__handle_duplex_callback_playback(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData, &pDevice->coreaudio.duplexRB);
21190 } else {
21191 ma_device__read_frames_from_client(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData);
21192 }
21193 }
21194
21195 #if defined(MA_DEBUG_OUTPUT)
21196 printf(" frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
21197 #endif
21198 } else {
21199 /*
21200 This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
21201 not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just
21202 output silence here.
21203 */
21204 MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
21205
21206 #if defined(MA_DEBUG_OUTPUT)
21207 printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
21208 #endif
21209 }
21210 }
21211 } else {
21212 /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */
21213
21214 /*
21215 For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
21216 very strange has happened and we're not going to support it.
21217 */
21218 if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) {
21219 ma_uint8 tempBuffer[4096];
21220 UInt32 iBuffer;
21221
21222 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) {
21223 ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat);
21224 ma_uint32 framesRemaining = frameCountPerBuffer;
21225
21226 while (framesRemaining > 0) {
21227 void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
21228 ma_uint32 iChannel;
21229 ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
21230 if (framesToRead > framesRemaining) {
21231 framesToRead = framesRemaining;
21232 }
21233
21234 if (pDevice->type == ma_device_type_duplex) {
21235 ma_device__handle_duplex_callback_playback(pDevice, framesToRead, tempBuffer, &pDevice->coreaudio.duplexRB);
21236 } else {
21237 ma_device__read_frames_from_client(pDevice, framesToRead, tempBuffer);
21238 }
21239
21240 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
21241 ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
21242 }
21243
21244 ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);
21245
21246 framesRemaining -= framesToRead;
21247 }
21248 }
21249 }
21250 }
21251
21252 (void)pActionFlags;
21253 (void)pTimeStamp;
21254 (void)busNumber;
21255
21256 return noErr;
21257}
21258
21259static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList)
21260{
21261 ma_device* pDevice = (ma_device*)pUserData;
21262 AudioBufferList* pRenderedBufferList;
21263 ma_stream_layout layout;
21264 OSStatus status;
21265
21266 MA_ASSERT(pDevice != NULL);
21267
21268 pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
21269 MA_ASSERT(pRenderedBufferList);
21270
21271 /* 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. */
21272 layout = ma_stream_layout_interleaved;
21273 if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) {
21274 layout = ma_stream_layout_deinterleaved;
21275 }
21276
21277#if defined(MA_DEBUG_OUTPUT)
21278 printf("INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers);
21279#endif
21280
21281 status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList);
21282 if (status != noErr) {
21283 #if defined(MA_DEBUG_OUTPUT)
21284 printf(" ERROR: AudioUnitRender() failed with %d\n", status);
21285 #endif
21286 return status;
21287 }
21288
21289 if (layout == ma_stream_layout_interleaved) {
21290 UInt32 iBuffer;
21291 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
21292 if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) {
21293 if (pDevice->type == ma_device_type_duplex) {
21294 ma_device__handle_duplex_callback_capture(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData, &pDevice->coreaudio.duplexRB);
21295 } else {
21296 ma_device__send_frames_to_client(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData);
21297 }
21298 #if defined(MA_DEBUG_OUTPUT)
21299 printf(" mDataByteSize=%d\n", pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
21300 #endif
21301 } else {
21302 /*
21303 This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
21304 not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams.
21305 */
21306 ma_uint8 silentBuffer[4096];
21307 ma_uint32 framesRemaining;
21308
21309 MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer));
21310
21311 framesRemaining = frameCount;
21312 while (framesRemaining > 0) {
21313 ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
21314 if (framesToSend > framesRemaining) {
21315 framesToSend = framesRemaining;
21316 }
21317
21318 if (pDevice->type == ma_device_type_duplex) {
21319 ma_device__handle_duplex_callback_capture(pDevice, framesToSend, silentBuffer, &pDevice->coreaudio.duplexRB);
21320 } else {
21321 ma_device__send_frames_to_client(pDevice, framesToSend, silentBuffer);
21322 }
21323
21324 framesRemaining -= framesToSend;
21325 }
21326
21327 #if defined(MA_DEBUG_OUTPUT)
21328 printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
21329 #endif
21330 }
21331 }
21332 } else {
21333 /* 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. */
21334
21335 /*
21336 For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
21337 very strange has happened and we're not going to support it.
21338 */
21339 if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) {
21340 ma_uint8 tempBuffer[4096];
21341 UInt32 iBuffer;
21342 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) {
21343 ma_uint32 framesRemaining = frameCount;
21344 while (framesRemaining > 0) {
21345 void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
21346 ma_uint32 iChannel;
21347 ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_sample(pDevice->capture.internalFormat);
21348 if (framesToSend > framesRemaining) {
21349 framesToSend = framesRemaining;
21350 }
21351
21352 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
21353 ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
21354 }
21355
21356 ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);
21357
21358 if (pDevice->type == ma_device_type_duplex) {
21359 ma_device__handle_duplex_callback_capture(pDevice, framesToSend, tempBuffer, &pDevice->coreaudio.duplexRB);
21360 } else {
21361 ma_device__send_frames_to_client(pDevice, framesToSend, tempBuffer);
21362 }
21363
21364 framesRemaining -= framesToSend;
21365 }
21366 }
21367 }
21368 }
21369
21370 (void)pActionFlags;
21371 (void)pTimeStamp;
21372 (void)busNumber;
21373 (void)frameCount;
21374 (void)pUnusedBufferList;
21375
21376 return noErr;
21377}
21378
21379static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element)
21380{
21381 ma_device* pDevice = (ma_device*)pUserData;
21382 MA_ASSERT(pDevice != NULL);
21383
21384 /*
21385 There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like
21386 AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit)
21387 can try waiting on the same lock. I'm going to try working around this by not calling any Core
21388 Audio APIs in the callback when the device has been stopped or uninitialized.
21389 */
21390 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) {
21391 ma_stop_proc onStop = pDevice->onStop;
21392 if (onStop) {
21393 onStop(pDevice);
21394 }
21395
21396 ma_event_signal(&pDevice->coreaudio.stopEvent);
21397 } else {
21398 UInt32 isRunning;
21399 UInt32 isRunningSize = sizeof(isRunning);
21400 OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize);
21401 if (status != noErr) {
21402 return; /* Don't really know what to do in this case... just ignore it, I suppose... */
21403 }
21404
21405 if (!isRunning) {
21406 ma_stop_proc onStop;
21407
21408 /*
21409 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:
21410
21411 1) When the device is unplugged, this will be called _before_ the default device change notification.
21412 2) When the device is changed via the default device change notification, this will be called _after_ the switch.
21413
21414 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.
21415 */
21416 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) ||
21417 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) {
21418 /*
21419 It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device
21420 via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the
21421 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
21422 hasn't!).
21423 */
21424 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
21425 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
21426 return;
21427 }
21428
21429 /*
21430 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
21431 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
21432 likely be successful in switching to the new device.
21433
21434 TODO: Try to predict if Core Audio will switch devices. If not, the onStop callback needs to be posted.
21435 */
21436 return;
21437 }
21438
21439 /* Getting here means we need to stop the device. */
21440 onStop = pDevice->onStop;
21441 if (onStop) {
21442 onStop(pDevice);
21443 }
21444 }
21445 }
21446
21447 (void)propertyID; /* Unused. */
21448}
21449
21450#if defined(MA_APPLE_DESKTOP)
21451static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0;
21452static ma_mutex g_DeviceTrackingMutex_CoreAudio;
21453static ma_device** g_ppTrackedDevices_CoreAudio = NULL;
21454static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0;
21455static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0;
21456
21457static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData)
21458{
21459 ma_device_type deviceType;
21460
21461 /* Not sure if I really need to check this, but it makes me feel better. */
21462 if (addressCount == 0) {
21463 return noErr;
21464 }
21465
21466 if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) {
21467 deviceType = ma_device_type_playback;
21468 } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) {
21469 deviceType = ma_device_type_capture;
21470 } else {
21471 return noErr; /* Should never hit this. */
21472 }
21473
21474 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
21475 {
21476 ma_uint32 iDevice;
21477 for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
21478 ma_result reinitResult;
21479 ma_device* pDevice;
21480
21481 pDevice = g_ppTrackedDevices_CoreAudio[iDevice];
21482 if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) {
21483 if (deviceType == ma_device_type_playback) {
21484 pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE;
21485 reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
21486 pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE;
21487 } else {
21488 pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE;
21489 reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
21490 pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE;
21491 }
21492
21493 if (reinitResult == MA_SUCCESS) {
21494 ma_device__post_init_setup(pDevice, deviceType);
21495
21496 /* Restart the device if required. If this fails we need to stop the device entirely. */
21497 if (ma_device__get_state(pDevice) == MA_STATE_STARTED) {
21498 OSStatus status;
21499 if (deviceType == ma_device_type_playback) {
21500 status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
21501 if (status != noErr) {
21502 if (pDevice->type == ma_device_type_duplex) {
21503 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
21504 }
21505 ma_device__set_state(pDevice, MA_STATE_STOPPED);
21506 }
21507 } else if (deviceType == ma_device_type_capture) {
21508 status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
21509 if (status != noErr) {
21510 if (pDevice->type == ma_device_type_duplex) {
21511 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
21512 }
21513 ma_device__set_state(pDevice, MA_STATE_STOPPED);
21514 }
21515 }
21516 }
21517 }
21518 }
21519 }
21520 }
21521 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
21522
21523 (void)objectID; /* Unused. */
21524 return noErr;
21525}
21526
21527static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext)
21528{
21529 MA_ASSERT(pContext != NULL);
21530
21531 if (ma_atomic_increment_32(&g_DeviceTrackingInitCounter_CoreAudio) == 1) {
21532 AudioObjectPropertyAddress propAddress;
21533 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
21534 propAddress.mElement = kAudioObjectPropertyElementMaster;
21535
21536 ma_mutex_init(pContext, &g_DeviceTrackingMutex_CoreAudio);
21537
21538 propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
21539 ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
21540
21541 propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
21542 ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
21543 }
21544
21545 return MA_SUCCESS;
21546}
21547
21548static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext)
21549{
21550 MA_ASSERT(pContext != NULL);
21551
21552 if (ma_atomic_decrement_32(&g_DeviceTrackingInitCounter_CoreAudio) == 0) {
21553 AudioObjectPropertyAddress propAddress;
21554 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
21555 propAddress.mElement = kAudioObjectPropertyElementMaster;
21556
21557 propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
21558 ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
21559
21560 propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
21561 ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
21562
21563 /* At this point there should be no tracked devices. If so there's an error somewhere. */
21564 MA_ASSERT(g_ppTrackedDevices_CoreAudio == NULL);
21565 MA_ASSERT(g_TrackedDeviceCount_CoreAudio == 0);
21566
21567 ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio);
21568 }
21569
21570 return MA_SUCCESS;
21571}
21572
21573static ma_result ma_device__track__coreaudio(ma_device* pDevice)
21574{
21575 ma_result result;
21576
21577 MA_ASSERT(pDevice != NULL);
21578
21579 result = ma_context__init_device_tracking__coreaudio(pDevice->pContext);
21580 if (result != MA_SUCCESS) {
21581 return result;
21582 }
21583
21584 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
21585 {
21586 /* Allocate memory if required. */
21587 if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) {
21588 ma_uint32 oldCap;
21589 ma_uint32 newCap;
21590 ma_device** ppNewDevices;
21591
21592 oldCap = g_TrackedDeviceCap_CoreAudio;
21593 newCap = g_TrackedDeviceCap_CoreAudio * 2;
21594 if (newCap == 0) {
21595 newCap = 1;
21596 }
21597
21598 ppNewDevices = (ma_device**)ma__realloc_from_callbacks(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, sizeof(*g_ppTrackedDevices_CoreAudio)*oldCap, &pDevice->pContext->allocationCallbacks);
21599 if (ppNewDevices == NULL) {
21600 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
21601 return MA_OUT_OF_MEMORY;
21602 }
21603
21604 g_ppTrackedDevices_CoreAudio = ppNewDevices;
21605 g_TrackedDeviceCap_CoreAudio = newCap;
21606 }
21607
21608 g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice;
21609 g_TrackedDeviceCount_CoreAudio += 1;
21610 }
21611 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
21612
21613 return MA_SUCCESS;
21614}
21615
21616static ma_result ma_device__untrack__coreaudio(ma_device* pDevice)
21617{
21618 ma_result result;
21619
21620 MA_ASSERT(pDevice != NULL);
21621
21622 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
21623 {
21624 ma_uint32 iDevice;
21625 for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
21626 if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) {
21627 /* We've found the device. We now need to remove it from the list. */
21628 ma_uint32 jDevice;
21629 for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) {
21630 g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1];
21631 }
21632
21633 g_TrackedDeviceCount_CoreAudio -= 1;
21634
21635 /* If there's nothing else in the list we need to free memory. */
21636 if (g_TrackedDeviceCount_CoreAudio == 0) {
21637 ma__free_from_callbacks(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks);
21638 g_ppTrackedDevices_CoreAudio = NULL;
21639 g_TrackedDeviceCap_CoreAudio = 0;
21640 }
21641
21642 break;
21643 }
21644 }
21645 }
21646 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
21647
21648 result = ma_context__uninit_device_tracking__coreaudio(pDevice->pContext);
21649 if (result != MA_SUCCESS) {
21650 return result;
21651 }
21652
21653 return MA_SUCCESS;
21654}
21655#endif
21656
21657#if defined(MA_APPLE_MOBILE)
21658@interface ma_router_change_handler:NSObject {
21659 ma_device* m_pDevice;
21660}
21661@end
21662
21663@implementation ma_router_change_handler
21664-(id)init:(ma_device*)pDevice
21665{
21666 self = [super init];
21667 m_pDevice = pDevice;
21668
21669 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
21670
21671 return self;
21672}
21673
21674-(void)dealloc
21675{
21676 [self remove_handler];
21677}
21678
21679-(void)remove_handler
21680{
21681 [[NSNotificationCenter defaultCenter] removeObserver:self name:@"AVAudioSessionRouteChangeNotification" object:nil];
21682}
21683
21684-(void)handle_route_change:(NSNotification*)pNotification
21685{
21686 AVAudioSession* pSession = [AVAudioSession sharedInstance];
21687
21688 NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
21689 switch (reason)
21690 {
21691 case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
21692 {
21693 #if defined(MA_DEBUG_OUTPUT)
21694 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n");
21695 #endif
21696 } break;
21697
21698 case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
21699 {
21700 #if defined(MA_DEBUG_OUTPUT)
21701 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n");
21702 #endif
21703 } break;
21704
21705 case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
21706 {
21707 #if defined(MA_DEBUG_OUTPUT)
21708 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n");
21709 #endif
21710 } break;
21711
21712 case AVAudioSessionRouteChangeReasonWakeFromSleep:
21713 {
21714 #if defined(MA_DEBUG_OUTPUT)
21715 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n");
21716 #endif
21717 } break;
21718
21719 case AVAudioSessionRouteChangeReasonOverride:
21720 {
21721 #if defined(MA_DEBUG_OUTPUT)
21722 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n");
21723 #endif
21724 } break;
21725
21726 case AVAudioSessionRouteChangeReasonCategoryChange:
21727 {
21728 #if defined(MA_DEBUG_OUTPUT)
21729 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n");
21730 #endif
21731 } break;
21732
21733 case AVAudioSessionRouteChangeReasonUnknown:
21734 default:
21735 {
21736 #if defined(MA_DEBUG_OUTPUT)
21737 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n");
21738 #endif
21739 } break;
21740 }
21741
21742 m_pDevice->sampleRate = (ma_uint32)pSession.sampleRate;
21743
21744 if (m_pDevice->type == ma_device_type_capture || m_pDevice->type == ma_device_type_duplex) {
21745 m_pDevice->capture.channels = (ma_uint32)pSession.inputNumberOfChannels;
21746 ma_device__post_init_setup(m_pDevice, ma_device_type_capture);
21747 }
21748 if (m_pDevice->type == ma_device_type_playback || m_pDevice->type == ma_device_type_duplex) {
21749 m_pDevice->playback.channels = (ma_uint32)pSession.outputNumberOfChannels;
21750 ma_device__post_init_setup(m_pDevice, ma_device_type_playback);
21751 }
21752}
21753@end
21754#endif
21755
21756static void ma_device_uninit__coreaudio(ma_device* pDevice)
21757{
21758 MA_ASSERT(pDevice != NULL);
21759 MA_ASSERT(ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED);
21760
21761#if defined(MA_APPLE_DESKTOP)
21762 /*
21763 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
21764 just gracefully ignore it.
21765 */
21766 ma_device__untrack__coreaudio(pDevice);
21767#endif
21768#if defined(MA_APPLE_MOBILE)
21769 if (pDevice->coreaudio.pRouteChangeHandler != NULL) {
21770 ma_router_change_handler* pRouteChangeHandler = (__bridge_transfer ma_router_change_handler*)pDevice->coreaudio.pRouteChangeHandler;
21771 [pRouteChangeHandler remove_handler];
21772 }
21773#endif
21774
21775 if (pDevice->coreaudio.audioUnitCapture != NULL) {
21776 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
21777 }
21778 if (pDevice->coreaudio.audioUnitPlayback != NULL) {
21779 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
21780 }
21781
21782 if (pDevice->coreaudio.pAudioBufferList) {
21783 ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
21784 }
21785
21786 if (pDevice->type == ma_device_type_duplex) {
21787 ma_pcm_rb_uninit(&pDevice->coreaudio.duplexRB);
21788 }
21789}
21790
21791typedef struct
21792{
21793 /* Input. */
21794 ma_format formatIn;
21795 ma_uint32 channelsIn;
21796 ma_uint32 sampleRateIn;
21797 ma_channel channelMapIn[MA_MAX_CHANNELS];
21798 ma_uint32 periodSizeInFramesIn;
21799 ma_uint32 periodSizeInMillisecondsIn;
21800 ma_uint32 periodsIn;
21801 ma_bool32 usingDefaultFormat;
21802 ma_bool32 usingDefaultChannels;
21803 ma_bool32 usingDefaultSampleRate;
21804 ma_bool32 usingDefaultChannelMap;
21805 ma_share_mode shareMode;
21806 ma_bool32 registerStopEvent;
21807
21808 /* Output. */
21809#if defined(MA_APPLE_DESKTOP)
21810 AudioObjectID deviceObjectID;
21811#endif
21812 AudioComponent component;
21813 AudioUnit audioUnit;
21814 AudioBufferList* pAudioBufferList; /* Only used for input devices. */
21815 ma_format formatOut;
21816 ma_uint32 channelsOut;
21817 ma_uint32 sampleRateOut;
21818 ma_channel channelMapOut[MA_MAX_CHANNELS];
21819 ma_uint32 periodSizeInFramesOut;
21820 ma_uint32 periodsOut;
21821 char deviceName[256];
21822} ma_device_init_internal_data__coreaudio;
21823
21824static 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. */
21825{
21826 ma_result result;
21827 OSStatus status;
21828 UInt32 enableIOFlag;
21829 AudioStreamBasicDescription bestFormat;
21830 ma_uint32 actualPeriodSizeInFrames;
21831 AURenderCallbackStruct callbackInfo;
21832#if defined(MA_APPLE_DESKTOP)
21833 AudioObjectID deviceObjectID;
21834#endif
21835
21836 /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */
21837 if (deviceType == ma_device_type_duplex) {
21838 return MA_INVALID_ARGS;
21839 }
21840
21841 MA_ASSERT(pContext != NULL);
21842 MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture);
21843
21844#if defined(MA_APPLE_DESKTOP)
21845 pData->deviceObjectID = 0;
21846#endif
21847 pData->component = NULL;
21848 pData->audioUnit = NULL;
21849 pData->pAudioBufferList = NULL;
21850
21851#if defined(MA_APPLE_DESKTOP)
21852 result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
21853 if (result != MA_SUCCESS) {
21854 return result;
21855 }
21856
21857 pData->deviceObjectID = deviceObjectID;
21858#endif
21859
21860 /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */
21861 pData->periodsOut = pData->periodsIn;
21862 if (pData->periodsOut == 0) {
21863 pData->periodsOut = MA_DEFAULT_PERIODS;
21864 }
21865 if (pData->periodsOut > 16) {
21866 pData->periodsOut = 16;
21867 }
21868
21869
21870 /* Audio unit. */
21871 status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit);
21872 if (status != noErr) {
21873 return ma_result_from_OSStatus(status);
21874 }
21875
21876
21877 /* 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. */
21878 enableIOFlag = 1;
21879 if (deviceType == ma_device_type_capture) {
21880 enableIOFlag = 0;
21881 }
21882
21883 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
21884 if (status != noErr) {
21885 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
21886 return ma_result_from_OSStatus(status);
21887 }
21888
21889 enableIOFlag = (enableIOFlag == 0) ? 1 : 0;
21890 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
21891 if (status != noErr) {
21892 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
21893 return ma_result_from_OSStatus(status);
21894 }
21895
21896
21897 /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */
21898#if defined(MA_APPLE_DESKTOP)
21899 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS, &deviceObjectID, sizeof(AudioDeviceID));
21900 if (status != noErr) {
21901 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
21902 return ma_result_from_OSStatus(result);
21903 }
21904#endif
21905
21906 /*
21907 Format. This is the hardest part of initialization because there's a few variables to take into account.
21908 1) The format must be supported by the device.
21909 2) The format must be supported miniaudio.
21910 3) There's a priority that miniaudio prefers.
21911
21912 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
21913 most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same
21914 for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely.
21915
21916 On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to.
21917 */
21918 {
21919 AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
21920 AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
21921
21922 #if defined(MA_APPLE_DESKTOP)
21923 AudioStreamBasicDescription origFormat;
21924 UInt32 origFormatSize;
21925
21926 result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, pData->usingDefaultFormat, pData->usingDefaultChannels, pData->usingDefaultSampleRate, &bestFormat);
21927 if (result != MA_SUCCESS) {
21928 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
21929 return result;
21930 }
21931
21932 /* From what I can see, Apple's documentation implies that we should keep the sample rate consistent. */
21933 origFormatSize = sizeof(origFormat);
21934 if (deviceType == ma_device_type_playback) {
21935 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize);
21936 } else {
21937 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize);
21938 }
21939
21940 if (status != noErr) {
21941 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
21942 return result;
21943 }
21944
21945 bestFormat.mSampleRate = origFormat.mSampleRate;
21946
21947 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
21948 if (status != noErr) {
21949 /* We failed to set the format, so fall back to the current format of the audio unit. */
21950 bestFormat = origFormat;
21951 }
21952 #else
21953 UInt32 propSize = sizeof(bestFormat);
21954 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
21955 if (status != noErr) {
21956 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
21957 return ma_result_from_OSStatus(status);
21958 }
21959
21960 /*
21961 Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try
21962 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
21963 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
21964 can tell, it looks like the sample rate is shared between playback and capture for everything.
21965 */
21966 @autoreleasepool {
21967 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
21968 MA_ASSERT(pAudioSession != NULL);
21969
21970 [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil];
21971 bestFormat.mSampleRate = pAudioSession.sampleRate;
21972
21973 /*
21974 I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with
21975 AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead.
21976 */
21977 if (deviceType == ma_device_type_playback) {
21978 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels;
21979 }
21980 if (deviceType == ma_device_type_capture) {
21981 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels;
21982 }
21983 }
21984
21985 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
21986 if (status != noErr) {
21987 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
21988 return ma_result_from_OSStatus(status);
21989 }
21990 #endif
21991
21992 result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut);
21993 if (result != MA_SUCCESS) {
21994 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
21995 return result;
21996 }
21997
21998 if (pData->formatOut == ma_format_unknown) {
21999 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
22000 return MA_FORMAT_NOT_SUPPORTED;
22001 }
22002
22003 pData->channelsOut = bestFormat.mChannelsPerFrame;
22004 pData->sampleRateOut = bestFormat.mSampleRate;
22005 }
22006
22007 /*
22008 Internal channel map. This is weird in my testing. If I use the AudioObject to get the
22009 channel map, the channel descriptions are set to "Unknown" for some reason. To work around
22010 this it looks like retrieving it from the AudioUnit will work. However, and this is where
22011 it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore
22012 I'm going to fall back to a default assumption in these cases.
22013 */
22014#if defined(MA_APPLE_DESKTOP)
22015 result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut);
22016 if (result != MA_SUCCESS) {
22017 #if 0
22018 /* Try falling back to the channel map from the AudioObject. */
22019 result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut);
22020 if (result != MA_SUCCESS) {
22021 return result;
22022 }
22023 #else
22024 /* Fall back to default assumptions. */
22025 ma_get_standard_channel_map(ma_standard_channel_map_default, pData->channelsOut, pData->channelMapOut);
22026 #endif
22027 }
22028#else
22029 /* TODO: Figure out how to get the channel map using AVAudioSession. */
22030 ma_get_standard_channel_map(ma_standard_channel_map_default, pData->channelsOut, pData->channelMapOut);
22031#endif
22032
22033
22034 /* Buffer size. Not allowing this to be configurable on iOS. */
22035 actualPeriodSizeInFrames = pData->periodSizeInFramesIn;
22036
22037#if defined(MA_APPLE_DESKTOP)
22038 if (actualPeriodSizeInFrames == 0) {
22039 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut);
22040 }
22041
22042 result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames);
22043 if (result != MA_SUCCESS) {
22044 return result;
22045 }
22046
22047 pData->periodSizeInFramesOut = actualPeriodSizeInFrames;
22048#else
22049 actualPeriodSizeInFrames = 2048;
22050 pData->periodSizeInFramesOut = actualPeriodSizeInFrames;
22051#endif
22052
22053
22054 /*
22055 During testing I discovered that the buffer size can be too big. You'll get an error like this:
22056
22057 kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512
22058
22059 Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that
22060 of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice.
22061 */
22062 {
22063 /*AudioUnitScope propScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
22064 AudioUnitElement propBus = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
22065
22066 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, propScope, propBus, &actualBufferSizeInFrames, sizeof(actualBufferSizeInFrames));
22067 if (status != noErr) {
22068 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
22069 return ma_result_from_OSStatus(status);
22070 }*/
22071
22072 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames));
22073 if (status != noErr) {
22074 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
22075 return ma_result_from_OSStatus(status);
22076 }
22077 }
22078
22079 /* We need a buffer list if this is an input device. We render into this in the input callback. */
22080 if (deviceType == ma_device_type_capture) {
22081 ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
22082 size_t allocationSize;
22083 AudioBufferList* pBufferList;
22084
22085 allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */
22086 if (isInterleaved) {
22087 /* Interleaved case. This is the simple case because we just have one buffer. */
22088 allocationSize += sizeof(AudioBuffer) * 1;
22089 allocationSize += actualPeriodSizeInFrames * ma_get_bytes_per_frame(pData->formatOut, pData->channelsOut);
22090 } else {
22091 /* Non-interleaved case. This is the more complex case because there's more than one buffer. */
22092 allocationSize += sizeof(AudioBuffer) * pData->channelsOut;
22093 allocationSize += actualPeriodSizeInFrames * ma_get_bytes_per_sample(pData->formatOut) * pData->channelsOut;
22094 }
22095
22096 pBufferList = (AudioBufferList*)ma__malloc_from_callbacks(allocationSize, &pContext->allocationCallbacks);
22097 if (pBufferList == NULL) {
22098 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
22099 return MA_OUT_OF_MEMORY;
22100 }
22101
22102 if (isInterleaved) {
22103 pBufferList->mNumberBuffers = 1;
22104 pBufferList->mBuffers[0].mNumberChannels = pData->channelsOut;
22105 pBufferList->mBuffers[0].mDataByteSize = actualPeriodSizeInFrames * ma_get_bytes_per_frame(pData->formatOut, pData->channelsOut);
22106 pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList);
22107 } else {
22108 ma_uint32 iBuffer;
22109 pBufferList->mNumberBuffers = pData->channelsOut;
22110 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
22111 pBufferList->mBuffers[iBuffer].mNumberChannels = 1;
22112 pBufferList->mBuffers[iBuffer].mDataByteSize = actualPeriodSizeInFrames * ma_get_bytes_per_sample(pData->formatOut);
22113 pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * pData->channelsOut)) + (actualPeriodSizeInFrames * ma_get_bytes_per_sample(pData->formatOut) * iBuffer);
22114 }
22115 }
22116
22117 pData->pAudioBufferList = pBufferList;
22118 }
22119
22120 /* Callbacks. */
22121 callbackInfo.inputProcRefCon = pDevice_DoNotReference;
22122 if (deviceType == ma_device_type_playback) {
22123 callbackInfo.inputProc = ma_on_output__coreaudio;
22124 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, MA_COREAUDIO_OUTPUT_BUS, &callbackInfo, sizeof(callbackInfo));
22125 if (status != noErr) {
22126 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
22127 return ma_result_from_OSStatus(status);
22128 }
22129 } else {
22130 callbackInfo.inputProc = ma_on_input__coreaudio;
22131 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, MA_COREAUDIO_INPUT_BUS, &callbackInfo, sizeof(callbackInfo));
22132 if (status != noErr) {
22133 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
22134 return ma_result_from_OSStatus(status);
22135 }
22136 }
22137
22138 /* We need to listen for stop events. */
22139 if (pData->registerStopEvent) {
22140 status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference);
22141 if (status != noErr) {
22142 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
22143 return ma_result_from_OSStatus(status);
22144 }
22145 }
22146
22147 /* Initialize the audio unit. */
22148 status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit);
22149 if (status != noErr) {
22150 ma__free_from_callbacks(pData->pAudioBufferList, &pContext->allocationCallbacks);
22151 pData->pAudioBufferList = NULL;
22152 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
22153 return ma_result_from_OSStatus(status);
22154 }
22155
22156 /* Grab the name. */
22157#if defined(MA_APPLE_DESKTOP)
22158 ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName);
22159#else
22160 if (deviceType == ma_device_type_playback) {
22161 ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
22162 } else {
22163 ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME);
22164 }
22165#endif
22166
22167 return result;
22168}
22169
22170static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit)
22171{
22172 ma_device_init_internal_data__coreaudio data;
22173 ma_result result;
22174
22175 /* This should only be called for playback or capture, not duplex. */
22176 if (deviceType == ma_device_type_duplex) {
22177 return MA_INVALID_ARGS;
22178 }
22179
22180 if (deviceType == ma_device_type_capture) {
22181 data.formatIn = pDevice->capture.format;
22182 data.channelsIn = pDevice->capture.channels;
22183 data.sampleRateIn = pDevice->sampleRate;
22184 MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
22185 data.usingDefaultFormat = pDevice->capture.usingDefaultFormat;
22186 data.usingDefaultChannels = pDevice->capture.usingDefaultChannels;
22187 data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
22188 data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap;
22189 data.shareMode = pDevice->capture.shareMode;
22190 data.registerStopEvent = MA_TRUE;
22191
22192 if (disposePreviousAudioUnit) {
22193 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
22194 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
22195 }
22196 if (pDevice->coreaudio.pAudioBufferList) {
22197 ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
22198 }
22199 } else if (deviceType == ma_device_type_playback) {
22200 data.formatIn = pDevice->playback.format;
22201 data.channelsIn = pDevice->playback.channels;
22202 data.sampleRateIn = pDevice->sampleRate;
22203 MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
22204 data.usingDefaultFormat = pDevice->playback.usingDefaultFormat;
22205 data.usingDefaultChannels = pDevice->playback.usingDefaultChannels;
22206 data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
22207 data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap;
22208 data.shareMode = pDevice->playback.shareMode;
22209 data.registerStopEvent = (pDevice->type != ma_device_type_duplex);
22210
22211 if (disposePreviousAudioUnit) {
22212 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
22213 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
22214 }
22215 }
22216 data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames;
22217 data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds;
22218 data.periodsIn = pDevice->coreaudio.originalPeriods;
22219
22220 /* Need at least 3 periods for duplex. */
22221 if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) {
22222 data.periodsIn = 3;
22223 }
22224
22225 result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice);
22226 if (result != MA_SUCCESS) {
22227 return result;
22228 }
22229
22230 if (deviceType == ma_device_type_capture) {
22231 #if defined(MA_APPLE_DESKTOP)
22232 pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
22233 #endif
22234 pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
22235 pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
22236
22237 pDevice->capture.internalFormat = data.formatOut;
22238 pDevice->capture.internalChannels = data.channelsOut;
22239 pDevice->capture.internalSampleRate = data.sampleRateOut;
22240 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
22241 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
22242 pDevice->capture.internalPeriods = data.periodsOut;
22243 } else if (deviceType == ma_device_type_playback) {
22244 #if defined(MA_APPLE_DESKTOP)
22245 pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
22246 #endif
22247 pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
22248
22249 pDevice->playback.internalFormat = data.formatOut;
22250 pDevice->playback.internalChannels = data.channelsOut;
22251 pDevice->playback.internalSampleRate = data.sampleRateOut;
22252 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
22253 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
22254 pDevice->playback.internalPeriods = data.periodsOut;
22255 }
22256
22257 return MA_SUCCESS;
22258}
22259
22260
22261static ma_result ma_device_init__coreaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
22262{
22263 ma_result result;
22264
22265 MA_ASSERT(pContext != NULL);
22266 MA_ASSERT(pConfig != NULL);
22267 MA_ASSERT(pDevice != NULL);
22268
22269 if (pConfig->deviceType == ma_device_type_loopback) {
22270 return MA_DEVICE_TYPE_NOT_SUPPORTED;
22271 }
22272
22273 /* No exclusive mode with the Core Audio backend for now. */
22274 if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive) ||
22275 ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive)) {
22276 return MA_SHARE_MODE_NOT_SUPPORTED;
22277 }
22278
22279 /* Capture needs to be initialized first. */
22280 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
22281 ma_device_init_internal_data__coreaudio data;
22282 data.formatIn = pConfig->capture.format;
22283 data.channelsIn = pConfig->capture.channels;
22284 data.sampleRateIn = pConfig->sampleRate;
22285 MA_COPY_MEMORY(data.channelMapIn, pConfig->capture.channelMap, sizeof(pConfig->capture.channelMap));
22286 data.usingDefaultFormat = pDevice->capture.usingDefaultFormat;
22287 data.usingDefaultChannels = pDevice->capture.usingDefaultChannels;
22288 data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
22289 data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap;
22290 data.shareMode = pConfig->capture.shareMode;
22291 data.periodSizeInFramesIn = pConfig->periodSizeInFrames;
22292 data.periodSizeInMillisecondsIn = pConfig->periodSizeInMilliseconds;
22293 data.periodsIn = pConfig->periods;
22294 data.registerStopEvent = MA_TRUE;
22295
22296 /* Need at least 3 periods for duplex. */
22297 if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) {
22298 data.periodsIn = 3;
22299 }
22300
22301 result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pConfig->capture.pDeviceID, &data, (void*)pDevice);
22302 if (result != MA_SUCCESS) {
22303 return result;
22304 }
22305
22306 pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL);
22307 #if defined(MA_APPLE_DESKTOP)
22308 pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
22309 #endif
22310 pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
22311 pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
22312
22313 pDevice->capture.internalFormat = data.formatOut;
22314 pDevice->capture.internalChannels = data.channelsOut;
22315 pDevice->capture.internalSampleRate = data.sampleRateOut;
22316 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
22317 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
22318 pDevice->capture.internalPeriods = data.periodsOut;
22319
22320 #if defined(MA_APPLE_DESKTOP)
22321 /*
22322 If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
22323 switch the device in the background.
22324 */
22325 if (pConfig->capture.pDeviceID == NULL) {
22326 ma_device__track__coreaudio(pDevice);
22327 }
22328 #endif
22329 }
22330
22331 /* Playback. */
22332 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
22333 ma_device_init_internal_data__coreaudio data;
22334 data.formatIn = pConfig->playback.format;
22335 data.channelsIn = pConfig->playback.channels;
22336 data.sampleRateIn = pConfig->sampleRate;
22337 MA_COPY_MEMORY(data.channelMapIn, pConfig->playback.channelMap, sizeof(pConfig->playback.channelMap));
22338 data.usingDefaultFormat = pDevice->playback.usingDefaultFormat;
22339 data.usingDefaultChannels = pDevice->playback.usingDefaultChannels;
22340 data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
22341 data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap;
22342 data.shareMode = pConfig->playback.shareMode;
22343
22344 /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */
22345 if (pConfig->deviceType == ma_device_type_duplex) {
22346 data.periodSizeInFramesIn = pDevice->capture.internalPeriodSizeInFrames;
22347 data.periodsIn = pDevice->capture.internalPeriods;
22348 data.registerStopEvent = MA_FALSE;
22349 } else {
22350 data.periodSizeInFramesIn = pConfig->periodSizeInFrames;
22351 data.periodSizeInMillisecondsIn = pConfig->periodSizeInMilliseconds;
22352 data.periodsIn = pConfig->periods;
22353 data.registerStopEvent = MA_TRUE;
22354 }
22355
22356 result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pConfig->playback.pDeviceID, &data, (void*)pDevice);
22357 if (result != MA_SUCCESS) {
22358 if (pConfig->deviceType == ma_device_type_duplex) {
22359 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
22360 if (pDevice->coreaudio.pAudioBufferList) {
22361 ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
22362 }
22363 }
22364 return result;
22365 }
22366
22367 pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL);
22368 #if defined(MA_APPLE_DESKTOP)
22369 pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
22370 #endif
22371 pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
22372
22373 pDevice->playback.internalFormat = data.formatOut;
22374 pDevice->playback.internalChannels = data.channelsOut;
22375 pDevice->playback.internalSampleRate = data.sampleRateOut;
22376 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
22377 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
22378 pDevice->playback.internalPeriods = data.periodsOut;
22379
22380 #if defined(MA_APPLE_DESKTOP)
22381 /*
22382 If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
22383 switch the device in the background.
22384 */
22385 if (pConfig->playback.pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pConfig->capture.pDeviceID != NULL)) {
22386 ma_device__track__coreaudio(pDevice);
22387 }
22388 #endif
22389 }
22390
22391 pDevice->coreaudio.originalPeriodSizeInFrames = pConfig->periodSizeInFrames;
22392 pDevice->coreaudio.originalPeriodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
22393 pDevice->coreaudio.originalPeriods = pConfig->periods;
22394
22395 /*
22396 When stopping the device, a callback is called on another thread. We need to wait for this callback
22397 before returning from ma_device_stop(). This event is used for this.
22398 */
22399 ma_event_init(pContext, &pDevice->coreaudio.stopEvent);
22400
22401 /* Need a ring buffer for duplex mode. */
22402 if (pConfig->deviceType == ma_device_type_duplex) {
22403 ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods);
22404 ma_result result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->pContext->allocationCallbacks, &pDevice->coreaudio.duplexRB);
22405 if (result != MA_SUCCESS) {
22406 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[Core Audio] Failed to initialize ring buffer.", result);
22407 }
22408
22409 /* We need a period to act as a buffer for cases where the playback and capture device's end up desyncing. */
22410 {
22411 ma_uint32 bufferSizeInFrames = rbSizeInFrames / pDevice->capture.internalPeriods;
22412 void* pBufferData;
22413 ma_pcm_rb_acquire_write(&pDevice->coreaudio.duplexRB, &bufferSizeInFrames, &pBufferData);
22414 {
22415 MA_ZERO_MEMORY(pBufferData, bufferSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
22416 }
22417 ma_pcm_rb_commit_write(&pDevice->coreaudio.duplexRB, bufferSizeInFrames, pBufferData);
22418 }
22419 }
22420
22421 /*
22422 We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done
22423 differently on non-Desktop Apple platforms.
22424 */
22425#if defined(MA_APPLE_MOBILE)
22426 pDevice->coreaudio.pRouteChangeHandler = (__bridge_retained void*)[[ma_router_change_handler alloc] init:pDevice];
22427#endif
22428
22429 return MA_SUCCESS;
22430}
22431
22432
22433static ma_result ma_device_start__coreaudio(ma_device* pDevice)
22434{
22435 MA_ASSERT(pDevice != NULL);
22436
22437 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
22438 OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
22439 if (status != noErr) {
22440 return ma_result_from_OSStatus(status);
22441 }
22442 }
22443
22444 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
22445 OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
22446 if (status != noErr) {
22447 if (pDevice->type == ma_device_type_duplex) {
22448 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
22449 }
22450 return ma_result_from_OSStatus(status);
22451 }
22452 }
22453
22454 return MA_SUCCESS;
22455}
22456
22457static ma_result ma_device_stop__coreaudio(ma_device* pDevice)
22458{
22459 MA_ASSERT(pDevice != NULL);
22460
22461 /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */
22462
22463 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
22464 OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
22465 if (status != noErr) {
22466 return ma_result_from_OSStatus(status);
22467 }
22468 }
22469
22470 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
22471 OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
22472 if (status != noErr) {
22473 return ma_result_from_OSStatus(status);
22474 }
22475 }
22476
22477 /* We need to wait for the callback to finish before returning. */
22478 ma_event_wait(&pDevice->coreaudio.stopEvent);
22479 return MA_SUCCESS;
22480}
22481
22482
22483static ma_result ma_context_uninit__coreaudio(ma_context* pContext)
22484{
22485 MA_ASSERT(pContext != NULL);
22486 MA_ASSERT(pContext->backend == ma_backend_coreaudio);
22487
22488#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
22489 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
22490 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
22491 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
22492#endif
22493
22494 (void)pContext;
22495 return MA_SUCCESS;
22496}
22497
22498#if defined(MA_APPLE_MOBILE)
22499static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category)
22500{
22501 /* The "default" and "none" categories are treated different and should not be used as an input into this function. */
22502 MA_ASSERT(category != ma_ios_session_category_default);
22503 MA_ASSERT(category != ma_ios_session_category_none);
22504
22505 switch (category) {
22506 case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient;
22507 case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient;
22508 case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback;
22509 case ma_ios_session_category_record: return AVAudioSessionCategoryRecord;
22510 case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord;
22511 case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute;
22512 case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient;
22513 case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient;
22514 default: return AVAudioSessionCategoryAmbient;
22515 }
22516}
22517#endif
22518
22519static ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma_context* pContext)
22520{
22521 MA_ASSERT(pConfig != NULL);
22522 MA_ASSERT(pContext != NULL);
22523
22524#if defined(MA_APPLE_MOBILE)
22525 @autoreleasepool {
22526 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
22527 AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions;
22528
22529 MA_ASSERT(pAudioSession != NULL);
22530
22531 if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) {
22532 /*
22533 I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails
22534 we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category.
22535 */
22536 #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH)
22537 options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
22538 #endif
22539
22540 if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) {
22541 /* Using PlayAndRecord */
22542 } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) {
22543 /* Using Playback */
22544 } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) {
22545 /* Using Record */
22546 } else {
22547 /* Leave as default? */
22548 }
22549 } else {
22550 if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) {
22551 if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) {
22552 return MA_INVALID_OPERATION; /* Failed to set session category. */
22553 }
22554 }
22555 }
22556 }
22557#endif
22558
22559#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
22560 pContext->coreaudio.hCoreFoundation = ma_dlopen(pContext, "CoreFoundation.framework/CoreFoundation");
22561 if (pContext->coreaudio.hCoreFoundation == NULL) {
22562 return MA_API_NOT_FOUND;
22563 }
22564
22565 pContext->coreaudio.CFStringGetCString = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFStringGetCString");
22566 pContext->coreaudio.CFRelease = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFRelease");
22567
22568
22569 pContext->coreaudio.hCoreAudio = ma_dlopen(pContext, "CoreAudio.framework/CoreAudio");
22570 if (pContext->coreaudio.hCoreAudio == NULL) {
22571 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
22572 return MA_API_NOT_FOUND;
22573 }
22574
22575 pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData");
22576 pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize");
22577 pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData");
22578 pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener");
22579 pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener");
22580
22581 /*
22582 It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still
22583 defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback.
22584 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
22585 AudioToolbox.
22586 */
22587 pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioUnit.framework/AudioUnit");
22588 if (pContext->coreaudio.hAudioUnit == NULL) {
22589 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
22590 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
22591 return MA_API_NOT_FOUND;
22592 }
22593
22594 if (ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) {
22595 /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */
22596 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
22597 pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioToolbox.framework/AudioToolbox");
22598 if (pContext->coreaudio.hAudioUnit == NULL) {
22599 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
22600 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
22601 return MA_API_NOT_FOUND;
22602 }
22603 }
22604
22605 pContext->coreaudio.AudioComponentFindNext = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext");
22606 pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose");
22607 pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew");
22608 pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart");
22609 pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop");
22610 pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener");
22611 pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo");
22612 pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty");
22613 pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty");
22614 pContext->coreaudio.AudioUnitInitialize = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitInitialize");
22615 pContext->coreaudio.AudioUnitRender = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitRender");
22616#else
22617 pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString;
22618 pContext->coreaudio.CFRelease = (ma_proc)CFRelease;
22619
22620 #if defined(MA_APPLE_DESKTOP)
22621 pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData;
22622 pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize;
22623 pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData;
22624 pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener;
22625 pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener;
22626 #endif
22627
22628 pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext;
22629 pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose;
22630 pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew;
22631 pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart;
22632 pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop;
22633 pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener;
22634 pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo;
22635 pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty;
22636 pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty;
22637 pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize;
22638 pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender;
22639#endif
22640
22641 pContext->isBackendAsynchronous = MA_TRUE;
22642
22643 pContext->onUninit = ma_context_uninit__coreaudio;
22644 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__coreaudio;
22645 pContext->onEnumDevices = ma_context_enumerate_devices__coreaudio;
22646 pContext->onGetDeviceInfo = ma_context_get_device_info__coreaudio;
22647 pContext->onDeviceInit = ma_device_init__coreaudio;
22648 pContext->onDeviceUninit = ma_device_uninit__coreaudio;
22649 pContext->onDeviceStart = ma_device_start__coreaudio;
22650 pContext->onDeviceStop = ma_device_stop__coreaudio;
22651
22652 /* Audio component. */
22653 {
22654 AudioComponentDescription desc;
22655 desc.componentType = kAudioUnitType_Output;
22656 #if defined(MA_APPLE_DESKTOP)
22657 desc.componentSubType = kAudioUnitSubType_HALOutput;
22658 #else
22659 desc.componentSubType = kAudioUnitSubType_RemoteIO;
22660 #endif
22661 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
22662 desc.componentFlags = 0;
22663 desc.componentFlagsMask = 0;
22664
22665 pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
22666 if (pContext->coreaudio.component == NULL) {
22667 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
22668 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
22669 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
22670 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
22671 #endif
22672 return MA_FAILED_TO_INIT_BACKEND;
22673 }
22674 }
22675
22676 return MA_SUCCESS;
22677}
22678#endif /* Core Audio */
22679
22680
22681
22682/******************************************************************************
22683
22684sndio Backend
22685
22686******************************************************************************/
22687#ifdef MA_HAS_SNDIO
22688#include <fcntl.h>
22689#include <sys/stat.h>
22690
22691/*
22692Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due
22693to miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device
22694just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's
22695demand for it or if I can get it tested and debugged more thoroughly.
22696*/
22697#if 0
22698#if defined(__NetBSD__) || defined(__OpenBSD__)
22699#include <sys/audioio.h>
22700#endif
22701#if defined(__FreeBSD__) || defined(__DragonFly__)
22702#include <sys/soundcard.h>
22703#endif
22704#endif
22705
22706#define MA_SIO_DEVANY "default"
22707#define MA_SIO_PLAY 1
22708#define MA_SIO_REC 2
22709#define MA_SIO_NENC 8
22710#define MA_SIO_NCHAN 8
22711#define MA_SIO_NRATE 16
22712#define MA_SIO_NCONF 4
22713
22714struct ma_sio_hdl; /* <-- Opaque */
22715
22716struct ma_sio_par
22717{
22718 unsigned int bits;
22719 unsigned int bps;
22720 unsigned int sig;
22721 unsigned int le;
22722 unsigned int msb;
22723 unsigned int rchan;
22724 unsigned int pchan;
22725 unsigned int rate;
22726 unsigned int bufsz;
22727 unsigned int xrun;
22728 unsigned int round;
22729 unsigned int appbufsz;
22730 int __pad[3];
22731 unsigned int __magic;
22732};
22733
22734struct ma_sio_enc
22735{
22736 unsigned int bits;
22737 unsigned int bps;
22738 unsigned int sig;
22739 unsigned int le;
22740 unsigned int msb;
22741};
22742
22743struct ma_sio_conf
22744{
22745 unsigned int enc;
22746 unsigned int rchan;
22747 unsigned int pchan;
22748 unsigned int rate;
22749};
22750
22751struct ma_sio_cap
22752{
22753 struct ma_sio_enc enc[MA_SIO_NENC];
22754 unsigned int rchan[MA_SIO_NCHAN];
22755 unsigned int pchan[MA_SIO_NCHAN];
22756 unsigned int rate[MA_SIO_NRATE];
22757 int __pad[7];
22758 unsigned int nconf;
22759 struct ma_sio_conf confs[MA_SIO_NCONF];
22760};
22761
22762typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int);
22763typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*);
22764typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
22765typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
22766typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*);
22767typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t);
22768typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t);
22769typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*);
22770typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*);
22771typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*);
22772
22773static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */
22774{
22775 ma_uint32 i;
22776 for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) {
22777 if (g_maStandardSampleRatePriorities[i] == sampleRate) {
22778 return i;
22779 }
22780 }
22781
22782 return (ma_uint32)-1;
22783}
22784
22785static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb)
22786{
22787 /* We only support native-endian right now. */
22788 if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) {
22789 return ma_format_unknown;
22790 }
22791
22792 if (bits == 8 && bps == 1 && sig == 0) {
22793 return ma_format_u8;
22794 }
22795 if (bits == 16 && bps == 2 && sig == 1) {
22796 return ma_format_s16;
22797 }
22798 if (bits == 24 && bps == 3 && sig == 1) {
22799 return ma_format_s24;
22800 }
22801 if (bits == 24 && bps == 4 && sig == 1 && msb == 0) {
22802 /*return ma_format_s24_32;*/
22803 }
22804 if (bits == 32 && bps == 4 && sig == 1) {
22805 return ma_format_s32;
22806 }
22807
22808 return ma_format_unknown;
22809}
22810
22811static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps)
22812{
22813 ma_format bestFormat;
22814 unsigned int iConfig;
22815
22816 MA_ASSERT(caps != NULL);
22817
22818 bestFormat = ma_format_unknown;
22819 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
22820 unsigned int iEncoding;
22821 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
22822 unsigned int bits;
22823 unsigned int bps;
22824 unsigned int sig;
22825 unsigned int le;
22826 unsigned int msb;
22827 ma_format format;
22828
22829 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
22830 continue;
22831 }
22832
22833 bits = caps->enc[iEncoding].bits;
22834 bps = caps->enc[iEncoding].bps;
22835 sig = caps->enc[iEncoding].sig;
22836 le = caps->enc[iEncoding].le;
22837 msb = caps->enc[iEncoding].msb;
22838 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
22839 if (format == ma_format_unknown) {
22840 continue; /* Format not supported. */
22841 }
22842
22843 if (bestFormat == ma_format_unknown) {
22844 bestFormat = format;
22845 } else {
22846 if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */
22847 bestFormat = format;
22848 }
22849 }
22850 }
22851 }
22852
22853 return ma_format_unknown;
22854}
22855
22856static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat)
22857{
22858 ma_uint32 maxChannels;
22859 unsigned int iConfig;
22860
22861 MA_ASSERT(caps != NULL);
22862 MA_ASSERT(requiredFormat != ma_format_unknown);
22863
22864 /* Just pick whatever configuration has the most channels. */
22865 maxChannels = 0;
22866 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
22867 /* The encoding should be of requiredFormat. */
22868 unsigned int iEncoding;
22869 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
22870 unsigned int iChannel;
22871 unsigned int bits;
22872 unsigned int bps;
22873 unsigned int sig;
22874 unsigned int le;
22875 unsigned int msb;
22876 ma_format format;
22877
22878 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
22879 continue;
22880 }
22881
22882 bits = caps->enc[iEncoding].bits;
22883 bps = caps->enc[iEncoding].bps;
22884 sig = caps->enc[iEncoding].sig;
22885 le = caps->enc[iEncoding].le;
22886 msb = caps->enc[iEncoding].msb;
22887 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
22888 if (format != requiredFormat) {
22889 continue;
22890 }
22891
22892 /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
22893 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
22894 unsigned int chan = 0;
22895 unsigned int channels;
22896
22897 if (deviceType == ma_device_type_playback) {
22898 chan = caps->confs[iConfig].pchan;
22899 } else {
22900 chan = caps->confs[iConfig].rchan;
22901 }
22902
22903 if ((chan & (1UL << iChannel)) == 0) {
22904 continue;
22905 }
22906
22907 if (deviceType == ma_device_type_playback) {
22908 channels = caps->pchan[iChannel];
22909 } else {
22910 channels = caps->rchan[iChannel];
22911 }
22912
22913 if (maxChannels < channels) {
22914 maxChannels = channels;
22915 }
22916 }
22917 }
22918 }
22919
22920 return maxChannels;
22921}
22922
22923static 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)
22924{
22925 ma_uint32 firstSampleRate;
22926 ma_uint32 bestSampleRate;
22927 unsigned int iConfig;
22928
22929 MA_ASSERT(caps != NULL);
22930 MA_ASSERT(requiredFormat != ma_format_unknown);
22931 MA_ASSERT(requiredChannels > 0);
22932 MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS);
22933
22934 firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */
22935 bestSampleRate = 0;
22936
22937 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
22938 /* The encoding should be of requiredFormat. */
22939 unsigned int iEncoding;
22940 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
22941 unsigned int iChannel;
22942 unsigned int bits;
22943 unsigned int bps;
22944 unsigned int sig;
22945 unsigned int le;
22946 unsigned int msb;
22947 ma_format format;
22948
22949 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
22950 continue;
22951 }
22952
22953 bits = caps->enc[iEncoding].bits;
22954 bps = caps->enc[iEncoding].bps;
22955 sig = caps->enc[iEncoding].sig;
22956 le = caps->enc[iEncoding].le;
22957 msb = caps->enc[iEncoding].msb;
22958 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
22959 if (format != requiredFormat) {
22960 continue;
22961 }
22962
22963 /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
22964 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
22965 unsigned int chan = 0;
22966 unsigned int channels;
22967 unsigned int iRate;
22968
22969 if (deviceType == ma_device_type_playback) {
22970 chan = caps->confs[iConfig].pchan;
22971 } else {
22972 chan = caps->confs[iConfig].rchan;
22973 }
22974
22975 if ((chan & (1UL << iChannel)) == 0) {
22976 continue;
22977 }
22978
22979 if (deviceType == ma_device_type_playback) {
22980 channels = caps->pchan[iChannel];
22981 } else {
22982 channels = caps->rchan[iChannel];
22983 }
22984
22985 if (channels != requiredChannels) {
22986 continue;
22987 }
22988
22989 /* Getting here means we have found a compatible encoding/channel pair. */
22990 for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
22991 ma_uint32 rate = (ma_uint32)caps->rate[iRate];
22992 ma_uint32 ratePriority;
22993
22994 if (firstSampleRate == 0) {
22995 firstSampleRate = rate;
22996 }
22997
22998 /* Disregard this rate if it's not a standard one. */
22999 ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate);
23000 if (ratePriority == (ma_uint32)-1) {
23001 continue;
23002 }
23003
23004 if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) { /* Lower = better. */
23005 bestSampleRate = rate;
23006 }
23007 }
23008 }
23009 }
23010 }
23011
23012 /* If a standard sample rate was not found just fall back to the first one that was iterated. */
23013 if (bestSampleRate == 0) {
23014 bestSampleRate = firstSampleRate;
23015 }
23016
23017 return bestSampleRate;
23018}
23019
23020
23021static ma_bool32 ma_context_is_device_id_equal__sndio(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
23022{
23023 MA_ASSERT(pContext != NULL);
23024 MA_ASSERT(pID0 != NULL);
23025 MA_ASSERT(pID1 != NULL);
23026 (void)pContext;
23027
23028 return ma_strcmp(pID0->sndio, pID1->sndio) == 0;
23029}
23030
23031static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
23032{
23033 ma_bool32 isTerminating = MA_FALSE;
23034 struct ma_sio_hdl* handle;
23035
23036 MA_ASSERT(pContext != NULL);
23037 MA_ASSERT(callback != NULL);
23038
23039 /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */
23040
23041 /* Playback. */
23042 if (!isTerminating) {
23043 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0);
23044 if (handle != NULL) {
23045 /* Supports playback. */
23046 ma_device_info deviceInfo;
23047 MA_ZERO_OBJECT(&deviceInfo);
23048 ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY);
23049 ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
23050
23051 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
23052
23053 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
23054 }
23055 }
23056
23057 /* Capture. */
23058 if (!isTerminating) {
23059 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0);
23060 if (handle != NULL) {
23061 /* Supports capture. */
23062 ma_device_info deviceInfo;
23063 MA_ZERO_OBJECT(&deviceInfo);
23064 ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default");
23065 ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME);
23066
23067 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
23068
23069 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
23070 }
23071 }
23072
23073 return MA_SUCCESS;
23074}
23075
23076static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
23077{
23078 char devid[256];
23079 struct ma_sio_hdl* handle;
23080 struct ma_sio_cap caps;
23081 unsigned int iConfig;
23082
23083 MA_ASSERT(pContext != NULL);
23084 (void)shareMode;
23085
23086 /* We need to open the device before we can get information about it. */
23087 if (pDeviceID == NULL) {
23088 ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY);
23089 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME);
23090 } else {
23091 ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio);
23092 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid);
23093 }
23094
23095 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0);
23096 if (handle == NULL) {
23097 return MA_NO_DEVICE;
23098 }
23099
23100 if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) {
23101 return MA_ERROR;
23102 }
23103
23104 for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) {
23105 /*
23106 The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give
23107 preference to some formats over others.
23108 */
23109 unsigned int iEncoding;
23110 unsigned int iChannel;
23111 unsigned int iRate;
23112
23113 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
23114 unsigned int bits;
23115 unsigned int bps;
23116 unsigned int sig;
23117 unsigned int le;
23118 unsigned int msb;
23119 ma_format format;
23120 ma_bool32 formatExists = MA_FALSE;
23121 ma_uint32 iExistingFormat;
23122
23123 if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) {
23124 continue;
23125 }
23126
23127 bits = caps.enc[iEncoding].bits;
23128 bps = caps.enc[iEncoding].bps;
23129 sig = caps.enc[iEncoding].sig;
23130 le = caps.enc[iEncoding].le;
23131 msb = caps.enc[iEncoding].msb;
23132 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
23133 if (format == ma_format_unknown) {
23134 continue; /* Format not supported. */
23135 }
23136
23137 /* Add this format if it doesn't already exist. */
23138 for (iExistingFormat = 0; iExistingFormat < pDeviceInfo->formatCount; iExistingFormat += 1) {
23139 if (pDeviceInfo->formats[iExistingFormat] == format) {
23140 formatExists = MA_TRUE;
23141 break;
23142 }
23143 }
23144
23145 if (!formatExists) {
23146 pDeviceInfo->formats[pDeviceInfo->formatCount++] = format;
23147 }
23148 }
23149
23150 /* Channels. */
23151 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
23152 unsigned int chan = 0;
23153 unsigned int channels;
23154
23155 if (deviceType == ma_device_type_playback) {
23156 chan = caps.confs[iConfig].pchan;
23157 } else {
23158 chan = caps.confs[iConfig].rchan;
23159 }
23160
23161 if ((chan & (1UL << iChannel)) == 0) {
23162 continue;
23163 }
23164
23165 if (deviceType == ma_device_type_playback) {
23166 channels = caps.pchan[iChannel];
23167 } else {
23168 channels = caps.rchan[iChannel];
23169 }
23170
23171 if (pDeviceInfo->minChannels > channels) {
23172 pDeviceInfo->minChannels = channels;
23173 }
23174 if (pDeviceInfo->maxChannels < channels) {
23175 pDeviceInfo->maxChannels = channels;
23176 }
23177 }
23178
23179 /* Sample rates. */
23180 for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
23181 if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) {
23182 unsigned int rate = caps.rate[iRate];
23183 if (pDeviceInfo->minSampleRate > rate) {
23184 pDeviceInfo->minSampleRate = rate;
23185 }
23186 if (pDeviceInfo->maxSampleRate < rate) {
23187 pDeviceInfo->maxSampleRate = rate;
23188 }
23189 }
23190 }
23191 }
23192
23193 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
23194 return MA_SUCCESS;
23195}
23196
23197static void ma_device_uninit__sndio(ma_device* pDevice)
23198{
23199 MA_ASSERT(pDevice != NULL);
23200
23201 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23202 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
23203 }
23204
23205 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23206 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
23207 }
23208}
23209
23210static ma_result ma_device_init_handle__sndio(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
23211{
23212 const char* pDeviceName;
23213 ma_ptr handle;
23214 int openFlags = 0;
23215 struct ma_sio_cap caps;
23216 struct ma_sio_par par;
23217 ma_device_id* pDeviceID;
23218 ma_format format;
23219 ma_uint32 channels;
23220 ma_uint32 sampleRate;
23221 ma_format internalFormat;
23222 ma_uint32 internalChannels;
23223 ma_uint32 internalSampleRate;
23224 ma_uint32 internalPeriodSizeInFrames;
23225 ma_uint32 internalPeriods;
23226
23227 MA_ASSERT(pContext != NULL);
23228 MA_ASSERT(pConfig != NULL);
23229 MA_ASSERT(deviceType != ma_device_type_duplex);
23230 MA_ASSERT(pDevice != NULL);
23231
23232 if (deviceType == ma_device_type_capture) {
23233 openFlags = MA_SIO_REC;
23234 pDeviceID = pConfig->capture.pDeviceID;
23235 format = pConfig->capture.format;
23236 channels = pConfig->capture.channels;
23237 sampleRate = pConfig->sampleRate;
23238 } else {
23239 openFlags = MA_SIO_PLAY;
23240 pDeviceID = pConfig->playback.pDeviceID;
23241 format = pConfig->playback.format;
23242 channels = pConfig->playback.channels;
23243 sampleRate = pConfig->sampleRate;
23244 }
23245
23246 pDeviceName = MA_SIO_DEVANY;
23247 if (pDeviceID != NULL) {
23248 pDeviceName = pDeviceID->sndio;
23249 }
23250
23251 handle = (ma_ptr)((ma_sio_open_proc)pContext->sndio.sio_open)(pDeviceName, openFlags, 0);
23252 if (handle == NULL) {
23253 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
23254 }
23255
23256 /* We need to retrieve the device caps to determine the most appropriate format to use. */
23257 if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) {
23258 ((ma_sio_close_proc)pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
23259 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps.", MA_ERROR);
23260 }
23261
23262 /*
23263 Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real
23264 way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this
23265 to the requested channels, regardless of whether or not the default channel count is requested.
23266
23267 For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the
23268 value returned by ma_find_best_channels_from_sio_cap__sndio().
23269 */
23270 if (deviceType == ma_device_type_capture) {
23271 if (pDevice->capture.usingDefaultFormat) {
23272 format = ma_find_best_format_from_sio_cap__sndio(&caps);
23273 }
23274 if (pDevice->capture.usingDefaultChannels) {
23275 if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
23276 channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
23277 }
23278 }
23279 } else {
23280 if (pDevice->playback.usingDefaultFormat) {
23281 format = ma_find_best_format_from_sio_cap__sndio(&caps);
23282 }
23283 if (pDevice->playback.usingDefaultChannels) {
23284 if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
23285 channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
23286 }
23287 }
23288 }
23289
23290 if (pDevice->usingDefaultSampleRate) {
23291 sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels);
23292 }
23293
23294
23295 ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par);
23296 par.msb = 0;
23297 par.le = ma_is_little_endian();
23298
23299 switch (format) {
23300 case ma_format_u8:
23301 {
23302 par.bits = 8;
23303 par.bps = 1;
23304 par.sig = 0;
23305 } break;
23306
23307 case ma_format_s24:
23308 {
23309 par.bits = 24;
23310 par.bps = 3;
23311 par.sig = 1;
23312 } break;
23313
23314 case ma_format_s32:
23315 {
23316 par.bits = 32;
23317 par.bps = 4;
23318 par.sig = 1;
23319 } break;
23320
23321 case ma_format_s16:
23322 case ma_format_f32:
23323 default:
23324 {
23325 par.bits = 16;
23326 par.bps = 2;
23327 par.sig = 1;
23328 } break;
23329 }
23330
23331 if (deviceType == ma_device_type_capture) {
23332 par.rchan = channels;
23333 } else {
23334 par.pchan = channels;
23335 }
23336
23337 par.rate = sampleRate;
23338
23339 internalPeriodSizeInFrames = pConfig->periodSizeInFrames;
23340 if (internalPeriodSizeInFrames == 0) {
23341 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, par.rate);
23342 }
23343
23344 par.round = internalPeriodSizeInFrames;
23345 par.appbufsz = par.round * pConfig->periods;
23346
23347 if (((ma_sio_setpar_proc)pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) {
23348 ((ma_sio_close_proc)pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
23349 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size.", MA_FORMAT_NOT_SUPPORTED);
23350 }
23351 if (((ma_sio_getpar_proc)pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) {
23352 ((ma_sio_close_proc)pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
23353 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size.", MA_FORMAT_NOT_SUPPORTED);
23354 }
23355
23356 internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb);
23357 internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan;
23358 internalSampleRate = par.rate;
23359 internalPeriods = par.appbufsz / par.round;
23360 internalPeriodSizeInFrames = par.round;
23361
23362 if (deviceType == ma_device_type_capture) {
23363 pDevice->sndio.handleCapture = handle;
23364 pDevice->capture.internalFormat = internalFormat;
23365 pDevice->capture.internalChannels = internalChannels;
23366 pDevice->capture.internalSampleRate = internalSampleRate;
23367 ma_get_standard_channel_map(ma_standard_channel_map_sndio, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
23368 pDevice->capture.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
23369 pDevice->capture.internalPeriods = internalPeriods;
23370 } else {
23371 pDevice->sndio.handlePlayback = handle;
23372 pDevice->playback.internalFormat = internalFormat;
23373 pDevice->playback.internalChannels = internalChannels;
23374 pDevice->playback.internalSampleRate = internalSampleRate;
23375 ma_get_standard_channel_map(ma_standard_channel_map_sndio, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
23376 pDevice->playback.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
23377 pDevice->playback.internalPeriods = internalPeriods;
23378 }
23379
23380#ifdef MA_DEBUG_OUTPUT
23381 printf("DEVICE INFO\n");
23382 printf(" Format: %s\n", ma_get_format_name(internalFormat));
23383 printf(" Channels: %d\n", internalChannels);
23384 printf(" Sample Rate: %d\n", internalSampleRate);
23385 printf(" Period Size: %d\n", internalPeriodSizeInFrames);
23386 printf(" Periods: %d\n", internalPeriods);
23387 printf(" appbufsz: %d\n", par.appbufsz);
23388 printf(" round: %d\n", par.round);
23389#endif
23390
23391 return MA_SUCCESS;
23392}
23393
23394static ma_result ma_device_init__sndio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
23395{
23396 MA_ASSERT(pDevice != NULL);
23397
23398 MA_ZERO_OBJECT(&pDevice->sndio);
23399
23400 if (pConfig->deviceType == ma_device_type_loopback) {
23401 return MA_DEVICE_TYPE_NOT_SUPPORTED;
23402 }
23403
23404 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
23405 ma_result result = ma_device_init_handle__sndio(pContext, pConfig, ma_device_type_capture, pDevice);
23406 if (result != MA_SUCCESS) {
23407 return result;
23408 }
23409 }
23410
23411 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
23412 ma_result result = ma_device_init_handle__sndio(pContext, pConfig, ma_device_type_playback, pDevice);
23413 if (result != MA_SUCCESS) {
23414 return result;
23415 }
23416 }
23417
23418 return MA_SUCCESS;
23419}
23420
23421static ma_result ma_device_stop__sndio(ma_device* pDevice)
23422{
23423 MA_ASSERT(pDevice != NULL);
23424
23425 /*
23426 From the documentation:
23427
23428 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
23429 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
23430 buffer is drained. In no case are samples in the play buffer discarded.
23431
23432 Therefore, sio_stop() performs all of the necessary draining for us.
23433 */
23434
23435 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23436 ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
23437 }
23438
23439 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23440 ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
23441 }
23442
23443 return MA_SUCCESS;
23444}
23445
23446static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
23447{
23448 int result;
23449
23450 if (pFramesWritten != NULL) {
23451 *pFramesWritten = 0;
23452 }
23453
23454 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));
23455 if (result == 0) {
23456 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
23457 }
23458
23459 if (pFramesWritten != NULL) {
23460 *pFramesWritten = frameCount;
23461 }
23462
23463 return MA_SUCCESS;
23464}
23465
23466static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
23467{
23468 int result;
23469
23470 if (pFramesRead != NULL) {
23471 *pFramesRead = 0;
23472 }
23473
23474 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));
23475 if (result == 0) {
23476 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
23477 }
23478
23479 if (pFramesRead != NULL) {
23480 *pFramesRead = frameCount;
23481 }
23482
23483 return MA_SUCCESS;
23484}
23485
23486static ma_result ma_device_main_loop__sndio(ma_device* pDevice)
23487{
23488 ma_result result = MA_SUCCESS;
23489 ma_bool32 exitLoop = MA_FALSE;
23490
23491 /* Devices need to be started here. */
23492 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23493 ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
23494 }
23495 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23496 ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */
23497 }
23498
23499 while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
23500 switch (pDevice->type)
23501 {
23502 case ma_device_type_duplex:
23503 {
23504 /* The process is: device_read -> convert -> callback -> convert -> device_write */
23505 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
23506 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
23507
23508 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
23509 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
23510 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
23511 ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
23512 ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
23513 ma_uint32 capturedDeviceFramesRemaining;
23514 ma_uint32 capturedDeviceFramesProcessed;
23515 ma_uint32 capturedDeviceFramesToProcess;
23516 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
23517 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
23518 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
23519 }
23520
23521 result = ma_device_read__sndio(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
23522 if (result != MA_SUCCESS) {
23523 exitLoop = MA_TRUE;
23524 break;
23525 }
23526
23527 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
23528 capturedDeviceFramesProcessed = 0;
23529
23530 for (;;) {
23531 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
23532 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
23533 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
23534 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
23535 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
23536 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
23537 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
23538
23539 /* Convert capture data from device format to client format. */
23540 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
23541 if (result != MA_SUCCESS) {
23542 break;
23543 }
23544
23545 /*
23546 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
23547 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
23548 */
23549 if (capturedClientFramesToProcessThisIteration == 0) {
23550 break;
23551 }
23552
23553 ma_device__on_data(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
23554
23555 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
23556 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
23557
23558 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
23559 for (;;) {
23560 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
23561 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
23562 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
23563 if (result != MA_SUCCESS) {
23564 break;
23565 }
23566
23567 result = ma_device_write__sndio(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
23568 if (result != MA_SUCCESS) {
23569 exitLoop = MA_TRUE;
23570 break;
23571 }
23572
23573 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
23574 if (capturedClientFramesToProcessThisIteration == 0) {
23575 break;
23576 }
23577 }
23578
23579 /* In case an error happened from ma_device_write__sndio()... */
23580 if (result != MA_SUCCESS) {
23581 exitLoop = MA_TRUE;
23582 break;
23583 }
23584 }
23585
23586 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
23587 }
23588 } break;
23589
23590 case ma_device_type_capture:
23591 {
23592 /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
23593 ma_uint8 intermediaryBuffer[8192];
23594 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
23595 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
23596 ma_uint32 framesReadThisPeriod = 0;
23597 while (framesReadThisPeriod < periodSizeInFrames) {
23598 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
23599 ma_uint32 framesProcessed;
23600 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
23601 if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
23602 framesToReadThisIteration = intermediaryBufferSizeInFrames;
23603 }
23604
23605 result = ma_device_read__sndio(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
23606 if (result != MA_SUCCESS) {
23607 exitLoop = MA_TRUE;
23608 break;
23609 }
23610
23611 ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
23612
23613 framesReadThisPeriod += framesProcessed;
23614 }
23615 } break;
23616
23617 case ma_device_type_playback:
23618 {
23619 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
23620 ma_uint8 intermediaryBuffer[8192];
23621 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
23622 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
23623 ma_uint32 framesWrittenThisPeriod = 0;
23624 while (framesWrittenThisPeriod < periodSizeInFrames) {
23625 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
23626 ma_uint32 framesProcessed;
23627 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
23628 if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
23629 framesToWriteThisIteration = intermediaryBufferSizeInFrames;
23630 }
23631
23632 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
23633
23634 result = ma_device_write__sndio(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
23635 if (result != MA_SUCCESS) {
23636 exitLoop = MA_TRUE;
23637 break;
23638 }
23639
23640 framesWrittenThisPeriod += framesProcessed;
23641 }
23642 } break;
23643
23644 /* To silence a warning. Will never hit this. */
23645 case ma_device_type_loopback:
23646 default: break;
23647 }
23648 }
23649
23650
23651 /* Here is where the device is stopped. */
23652 ma_device_stop__sndio(pDevice);
23653
23654 return result;
23655}
23656
23657static ma_result ma_context_uninit__sndio(ma_context* pContext)
23658{
23659 MA_ASSERT(pContext != NULL);
23660 MA_ASSERT(pContext->backend == ma_backend_sndio);
23661
23662 (void)pContext;
23663 return MA_SUCCESS;
23664}
23665
23666static ma_result ma_context_init__sndio(const ma_context_config* pConfig, ma_context* pContext)
23667{
23668#ifndef MA_NO_RUNTIME_LINKING
23669 const char* libsndioNames[] = {
23670 "libsndio.so"
23671 };
23672 size_t i;
23673
23674 for (i = 0; i < ma_countof(libsndioNames); ++i) {
23675 pContext->sndio.sndioSO = ma_dlopen(pContext, libsndioNames[i]);
23676 if (pContext->sndio.sndioSO != NULL) {
23677 break;
23678 }
23679 }
23680
23681 if (pContext->sndio.sndioSO == NULL) {
23682 return MA_NO_BACKEND;
23683 }
23684
23685 pContext->sndio.sio_open = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_open");
23686 pContext->sndio.sio_close = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_close");
23687 pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_setpar");
23688 pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getpar");
23689 pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getcap");
23690 pContext->sndio.sio_write = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_write");
23691 pContext->sndio.sio_read = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_read");
23692 pContext->sndio.sio_start = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_start");
23693 pContext->sndio.sio_stop = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_stop");
23694 pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_initpar");
23695#else
23696 pContext->sndio.sio_open = sio_open;
23697 pContext->sndio.sio_close = sio_close;
23698 pContext->sndio.sio_setpar = sio_setpar;
23699 pContext->sndio.sio_getpar = sio_getpar;
23700 pContext->sndio.sio_getcap = sio_getcap;
23701 pContext->sndio.sio_write = sio_write;
23702 pContext->sndio.sio_read = sio_read;
23703 pContext->sndio.sio_start = sio_start;
23704 pContext->sndio.sio_stop = sio_stop;
23705 pContext->sndio.sio_initpar = sio_initpar;
23706#endif
23707
23708 pContext->onUninit = ma_context_uninit__sndio;
23709 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__sndio;
23710 pContext->onEnumDevices = ma_context_enumerate_devices__sndio;
23711 pContext->onGetDeviceInfo = ma_context_get_device_info__sndio;
23712 pContext->onDeviceInit = ma_device_init__sndio;
23713 pContext->onDeviceUninit = ma_device_uninit__sndio;
23714 pContext->onDeviceStart = NULL; /* Not required for synchronous backends. */
23715 pContext->onDeviceStop = NULL; /* Not required for synchronous backends. */
23716 pContext->onDeviceMainLoop = ma_device_main_loop__sndio;
23717
23718 (void)pConfig;
23719 return MA_SUCCESS;
23720}
23721#endif /* sndio */
23722
23723
23724
23725/******************************************************************************
23726
23727audio(4) Backend
23728
23729******************************************************************************/
23730#ifdef MA_HAS_AUDIO4
23731#include <fcntl.h>
23732#include <poll.h>
23733#include <errno.h>
23734#include <sys/stat.h>
23735#include <sys/types.h>
23736#include <sys/ioctl.h>
23737#include <sys/audioio.h>
23738
23739#if defined(__OpenBSD__)
23740 #include <sys/param.h>
23741 #if defined(OpenBSD) && OpenBSD >= 201709
23742 #define MA_AUDIO4_USE_NEW_API
23743 #endif
23744#endif
23745
23746static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex)
23747{
23748 size_t baseLen;
23749
23750 MA_ASSERT(id != NULL);
23751 MA_ASSERT(idSize > 0);
23752 MA_ASSERT(deviceIndex >= 0);
23753
23754 baseLen = strlen(base);
23755 MA_ASSERT(idSize > baseLen);
23756
23757 ma_strcpy_s(id, idSize, base);
23758 ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10);
23759}
23760
23761static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut)
23762{
23763 size_t idLen;
23764 size_t baseLen;
23765 const char* deviceIndexStr;
23766
23767 MA_ASSERT(id != NULL);
23768 MA_ASSERT(base != NULL);
23769 MA_ASSERT(pIndexOut != NULL);
23770
23771 idLen = strlen(id);
23772 baseLen = strlen(base);
23773 if (idLen <= baseLen) {
23774 return MA_ERROR; /* Doesn't look like the id starts with the base. */
23775 }
23776
23777 if (strncmp(id, base, baseLen) != 0) {
23778 return MA_ERROR; /* ID does not begin with base. */
23779 }
23780
23781 deviceIndexStr = id + baseLen;
23782 if (deviceIndexStr[0] == '\0') {
23783 return MA_ERROR; /* No index specified in the ID. */
23784 }
23785
23786 if (pIndexOut) {
23787 *pIndexOut = atoi(deviceIndexStr);
23788 }
23789
23790 return MA_SUCCESS;
23791}
23792
23793static ma_bool32 ma_context_is_device_id_equal__audio4(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
23794{
23795 MA_ASSERT(pContext != NULL);
23796 MA_ASSERT(pID0 != NULL);
23797 MA_ASSERT(pID1 != NULL);
23798 (void)pContext;
23799
23800 return ma_strcmp(pID0->audio4, pID1->audio4) == 0;
23801}
23802
23803#if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
23804static ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision)
23805{
23806 if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) {
23807 return ma_format_u8;
23808 } else {
23809 if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) {
23810 if (precision == 16) {
23811 return ma_format_s16;
23812 } else if (precision == 24) {
23813 return ma_format_s24;
23814 } else if (precision == 32) {
23815 return ma_format_s32;
23816 }
23817 } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) {
23818 if (precision == 16) {
23819 return ma_format_s16;
23820 } else if (precision == 24) {
23821 return ma_format_s24;
23822 } else if (precision == 32) {
23823 return ma_format_s32;
23824 }
23825 }
23826 }
23827
23828 return ma_format_unknown; /* Encoding not supported. */
23829}
23830
23831static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision)
23832{
23833 MA_ASSERT(format != ma_format_unknown);
23834 MA_ASSERT(pEncoding != NULL);
23835 MA_ASSERT(pPrecision != NULL);
23836
23837 switch (format)
23838 {
23839 case ma_format_u8:
23840 {
23841 *pEncoding = AUDIO_ENCODING_ULINEAR;
23842 *pPrecision = 8;
23843 } break;
23844
23845 case ma_format_s24:
23846 {
23847 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
23848 *pPrecision = 24;
23849 } break;
23850
23851 case ma_format_s32:
23852 {
23853 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
23854 *pPrecision = 32;
23855 } break;
23856
23857 case ma_format_s16:
23858 case ma_format_f32:
23859 default:
23860 {
23861 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
23862 *pPrecision = 16;
23863 } break;
23864 }
23865}
23866
23867static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo)
23868{
23869 return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision);
23870}
23871#else
23872static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par)
23873{
23874 if (par->bits == 8 && par->bps == 1 && par->sig == 0) {
23875 return ma_format_u8;
23876 }
23877 if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) {
23878 return ma_format_s16;
23879 }
23880 if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) {
23881 return ma_format_s24;
23882 }
23883 if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) {
23884 return ma_format_f32;
23885 }
23886
23887 /* Format not supported. */
23888 return ma_format_unknown;
23889}
23890#endif
23891
23892static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pInfoOut)
23893{
23894 audio_device_t fdDevice;
23895#if !defined(MA_AUDIO4_USE_NEW_API)
23896 int counter = 0;
23897 audio_info_t fdInfo;
23898#else
23899 struct audio_swpar fdPar;
23900 ma_format format;
23901#endif
23902
23903 MA_ASSERT(pContext != NULL);
23904 MA_ASSERT(fd >= 0);
23905 MA_ASSERT(pInfoOut != NULL);
23906
23907 (void)pContext;
23908 (void)deviceType;
23909
23910 if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) {
23911 return MA_ERROR; /* Failed to retrieve device info. */
23912 }
23913
23914 /* Name. */
23915 ma_strcpy_s(pInfoOut->name, sizeof(pInfoOut->name), fdDevice.name);
23916
23917#if !defined(MA_AUDIO4_USE_NEW_API)
23918 /* Supported formats. We get this by looking at the encodings. */
23919 for (;;) {
23920 audio_encoding_t encoding;
23921 ma_format format;
23922
23923 MA_ZERO_OBJECT(&encoding);
23924 encoding.index = counter;
23925 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
23926 break;
23927 }
23928
23929 format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision);
23930 if (format != ma_format_unknown) {
23931 pInfoOut->formats[pInfoOut->formatCount++] = format;
23932 }
23933
23934 counter += 1;
23935 }
23936
23937 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
23938 return MA_ERROR;
23939 }
23940
23941 if (deviceType == ma_device_type_playback) {
23942 pInfoOut->minChannels = fdInfo.play.channels;
23943 pInfoOut->maxChannels = fdInfo.play.channels;
23944 pInfoOut->minSampleRate = fdInfo.play.sample_rate;
23945 pInfoOut->maxSampleRate = fdInfo.play.sample_rate;
23946 } else {
23947 pInfoOut->minChannels = fdInfo.record.channels;
23948 pInfoOut->maxChannels = fdInfo.record.channels;
23949 pInfoOut->minSampleRate = fdInfo.record.sample_rate;
23950 pInfoOut->maxSampleRate = fdInfo.record.sample_rate;
23951 }
23952#else
23953 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
23954 return MA_ERROR;
23955 }
23956
23957 format = ma_format_from_swpar__audio4(&fdPar);
23958 if (format == ma_format_unknown) {
23959 return MA_FORMAT_NOT_SUPPORTED;
23960 }
23961 pInfoOut->formats[pInfoOut->formatCount++] = format;
23962
23963 if (deviceType == ma_device_type_playback) {
23964 pInfoOut->minChannels = fdPar.pchan;
23965 pInfoOut->maxChannels = fdPar.pchan;
23966 } else {
23967 pInfoOut->minChannels = fdPar.rchan;
23968 pInfoOut->maxChannels = fdPar.rchan;
23969 }
23970
23971 pInfoOut->minSampleRate = fdPar.rate;
23972 pInfoOut->maxSampleRate = fdPar.rate;
23973#endif
23974
23975 return MA_SUCCESS;
23976}
23977
23978static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
23979{
23980 const int maxDevices = 64;
23981 char devpath[256];
23982 int iDevice;
23983
23984 MA_ASSERT(pContext != NULL);
23985 MA_ASSERT(callback != NULL);
23986
23987 /*
23988 Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN"
23989 version here since we can open it even when another process has control of the "/dev/audioN" device.
23990 */
23991 for (iDevice = 0; iDevice < maxDevices; ++iDevice) {
23992 struct stat st;
23993 int fd;
23994 ma_bool32 isTerminating = MA_FALSE;
23995
23996 ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl");
23997 ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10);
23998
23999 if (stat(devpath, &st) < 0) {
24000 break;
24001 }
24002
24003 /* The device exists, but we need to check if it's usable as playback and/or capture. */
24004
24005 /* Playback. */
24006 if (!isTerminating) {
24007 fd = open(devpath, O_RDONLY, 0);
24008 if (fd >= 0) {
24009 /* Supports playback. */
24010 ma_device_info deviceInfo;
24011 MA_ZERO_OBJECT(&deviceInfo);
24012 ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
24013 if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) {
24014 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
24015 }
24016
24017 close(fd);
24018 }
24019 }
24020
24021 /* Capture. */
24022 if (!isTerminating) {
24023 fd = open(devpath, O_WRONLY, 0);
24024 if (fd >= 0) {
24025 /* Supports capture. */
24026 ma_device_info deviceInfo;
24027 MA_ZERO_OBJECT(&deviceInfo);
24028 ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
24029 if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) {
24030 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
24031 }
24032
24033 close(fd);
24034 }
24035 }
24036
24037 if (isTerminating) {
24038 break;
24039 }
24040 }
24041
24042 return MA_SUCCESS;
24043}
24044
24045static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
24046{
24047 int fd = -1;
24048 int deviceIndex = -1;
24049 char ctlid[256];
24050 ma_result result;
24051
24052 MA_ASSERT(pContext != NULL);
24053 (void)shareMode;
24054
24055 /*
24056 We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number
24057 from the device ID which will be in "/dev/audioN" format.
24058 */
24059 if (pDeviceID == NULL) {
24060 /* Default device. */
24061 ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl");
24062 } else {
24063 /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */
24064 result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex);
24065 if (result != MA_SUCCESS) {
24066 return result;
24067 }
24068
24069 ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex);
24070 }
24071
24072 fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0);
24073 if (fd == -1) {
24074 return MA_NO_DEVICE;
24075 }
24076
24077 if (deviceIndex == -1) {
24078 ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio");
24079 } else {
24080 ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex);
24081 }
24082
24083 result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo);
24084
24085 close(fd);
24086 return result;
24087}
24088
24089static void ma_device_uninit__audio4(ma_device* pDevice)
24090{
24091 MA_ASSERT(pDevice != NULL);
24092
24093 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24094 close(pDevice->audio4.fdCapture);
24095 }
24096
24097 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24098 close(pDevice->audio4.fdPlayback);
24099 }
24100}
24101
24102static ma_result ma_device_init_fd__audio4(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
24103{
24104 const char* pDefaultDeviceNames[] = {
24105 "/dev/audio",
24106 "/dev/audio0"
24107 };
24108 int fd;
24109 int fdFlags = 0;
24110#if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
24111 audio_info_t fdInfo;
24112#else
24113 struct audio_swpar fdPar;
24114#endif
24115 ma_format internalFormat;
24116 ma_uint32 internalChannels;
24117 ma_uint32 internalSampleRate;
24118 ma_uint32 internalPeriodSizeInFrames;
24119 ma_uint32 internalPeriods;
24120
24121 MA_ASSERT(pContext != NULL);
24122 MA_ASSERT(pConfig != NULL);
24123 MA_ASSERT(deviceType != ma_device_type_duplex);
24124 MA_ASSERT(pDevice != NULL);
24125
24126 (void)pContext;
24127
24128 /* The first thing to do is open the file. */
24129 if (deviceType == ma_device_type_capture) {
24130 fdFlags = O_RDONLY;
24131 } else {
24132 fdFlags = O_WRONLY;
24133 }
24134 /*fdFlags |= O_NONBLOCK;*/
24135
24136 if ((deviceType == ma_device_type_capture && pConfig->capture.pDeviceID == NULL) || (deviceType == ma_device_type_playback && pConfig->playback.pDeviceID == NULL)) {
24137 /* Default device. */
24138 size_t iDevice;
24139 for (iDevice = 0; iDevice < ma_countof(pDefaultDeviceNames); ++iDevice) {
24140 fd = open(pDefaultDeviceNames[iDevice], fdFlags, 0);
24141 if (fd != -1) {
24142 break;
24143 }
24144 }
24145 } else {
24146 /* Specific device. */
24147 fd = open((deviceType == ma_device_type_capture) ? pConfig->capture.pDeviceID->audio4 : pConfig->playback.pDeviceID->audio4, fdFlags, 0);
24148 }
24149
24150 if (fd == -1) {
24151 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24152 }
24153
24154#if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
24155 AUDIO_INITINFO(&fdInfo);
24156
24157 /* We get the driver to do as much of the data conversion as possible. */
24158 if (deviceType == ma_device_type_capture) {
24159 fdInfo.mode = AUMODE_RECORD;
24160 ma_encoding_from_format__audio4(pConfig->capture.format, &fdInfo.record.encoding, &fdInfo.record.precision);
24161 fdInfo.record.channels = pConfig->capture.channels;
24162 fdInfo.record.sample_rate = pConfig->sampleRate;
24163 } else {
24164 fdInfo.mode = AUMODE_PLAY;
24165 ma_encoding_from_format__audio4(pConfig->playback.format, &fdInfo.play.encoding, &fdInfo.play.precision);
24166 fdInfo.play.channels = pConfig->playback.channels;
24167 fdInfo.play.sample_rate = pConfig->sampleRate;
24168 }
24169
24170 if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
24171 close(fd);
24172 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
24173 }
24174
24175 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
24176 close(fd);
24177 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
24178 }
24179
24180 if (deviceType == ma_device_type_capture) {
24181 internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record);
24182 internalChannels = fdInfo.record.channels;
24183 internalSampleRate = fdInfo.record.sample_rate;
24184 } else {
24185 internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play);
24186 internalChannels = fdInfo.play.channels;
24187 internalSampleRate = fdInfo.play.sample_rate;
24188 }
24189
24190 if (internalFormat == ma_format_unknown) {
24191 close(fd);
24192 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);
24193 }
24194
24195 /* Buffer. */
24196 {
24197 ma_uint32 internalPeriodSizeInBytes;
24198
24199 internalPeriodSizeInFrames = pConfig->periodSizeInFrames;
24200 if (internalPeriodSizeInFrames == 0) {
24201 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, internalSampleRate);
24202 }
24203
24204 internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
24205 if (internalPeriodSizeInBytes < 16) {
24206 internalPeriodSizeInBytes = 16;
24207 }
24208
24209 internalPeriods = pConfig->periods;
24210 if (internalPeriods < 2) {
24211 internalPeriods = 2;
24212 }
24213
24214 /* What miniaudio calls a period, audio4 calls a block. */
24215 AUDIO_INITINFO(&fdInfo);
24216 fdInfo.hiwat = internalPeriods;
24217 fdInfo.lowat = internalPeriods-1;
24218 fdInfo.blocksize = internalPeriodSizeInBytes;
24219 if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
24220 close(fd);
24221 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
24222 }
24223
24224 internalPeriods = fdInfo.hiwat;
24225 internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels);
24226 }
24227#else
24228 /* 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. */
24229 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
24230 close(fd);
24231 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters.", MA_FORMAT_NOT_SUPPORTED);
24232 }
24233
24234 internalFormat = ma_format_from_swpar__audio4(&fdPar);
24235 internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
24236 internalSampleRate = fdPar.rate;
24237
24238 if (internalFormat == ma_format_unknown) {
24239 close(fd);
24240 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);
24241 }
24242
24243 /* Buffer. */
24244 {
24245 ma_uint32 internalPeriodSizeInBytes;
24246
24247 internalPeriodSizeInFrames = pConfig->periodSizeInFrames;
24248 if (internalPeriodSizeInFrames == 0) {
24249 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, internalSampleRate);
24250 }
24251
24252 /* What miniaudio calls a period, audio4 calls a block. */
24253 internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
24254 if (internalPeriodSizeInBytes < 16) {
24255 internalPeriodSizeInBytes = 16;
24256 }
24257
24258 fdPar.nblks = pConfig->periods;
24259 fdPar.round = internalPeriodSizeInBytes;
24260
24261 if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) {
24262 close(fd);
24263 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters.", MA_FORMAT_NOT_SUPPORTED);
24264 }
24265
24266 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
24267 close(fd);
24268 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters.", MA_FORMAT_NOT_SUPPORTED);
24269 }
24270 }
24271
24272 internalFormat = ma_format_from_swpar__audio4(&fdPar);
24273 internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
24274 internalSampleRate = fdPar.rate;
24275 internalPeriods = fdPar.nblks;
24276 internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels);
24277#endif
24278
24279 if (internalFormat == ma_format_unknown) {
24280 close(fd);
24281 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);
24282 }
24283
24284 if (deviceType == ma_device_type_capture) {
24285 pDevice->audio4.fdCapture = fd;
24286 pDevice->capture.internalFormat = internalFormat;
24287 pDevice->capture.internalChannels = internalChannels;
24288 pDevice->capture.internalSampleRate = internalSampleRate;
24289 ma_get_standard_channel_map(ma_standard_channel_map_sound4, internalChannels, pDevice->capture.internalChannelMap);
24290 pDevice->capture.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
24291 pDevice->capture.internalPeriods = internalPeriods;
24292 } else {
24293 pDevice->audio4.fdPlayback = fd;
24294 pDevice->playback.internalFormat = internalFormat;
24295 pDevice->playback.internalChannels = internalChannels;
24296 pDevice->playback.internalSampleRate = internalSampleRate;
24297 ma_get_standard_channel_map(ma_standard_channel_map_sound4, internalChannels, pDevice->playback.internalChannelMap);
24298 pDevice->playback.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
24299 pDevice->playback.internalPeriods = internalPeriods;
24300 }
24301
24302 return MA_SUCCESS;
24303}
24304
24305static ma_result ma_device_init__audio4(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
24306{
24307 MA_ASSERT(pDevice != NULL);
24308
24309 MA_ZERO_OBJECT(&pDevice->audio4);
24310
24311 if (pConfig->deviceType == ma_device_type_loopback) {
24312 return MA_DEVICE_TYPE_NOT_SUPPORTED;
24313 }
24314
24315 pDevice->audio4.fdCapture = -1;
24316 pDevice->audio4.fdPlayback = -1;
24317
24318 /*
24319 The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD
24320 introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as
24321 I'm aware.
24322 */
24323#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000
24324 /* NetBSD 8.0+ */
24325 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
24326 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
24327 return MA_SHARE_MODE_NOT_SUPPORTED;
24328 }
24329#else
24330 /* All other flavors. */
24331#endif
24332
24333 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
24334 ma_result result = ma_device_init_fd__audio4(pContext, pConfig, ma_device_type_capture, pDevice);
24335 if (result != MA_SUCCESS) {
24336 return result;
24337 }
24338 }
24339
24340 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
24341 ma_result result = ma_device_init_fd__audio4(pContext, pConfig, ma_device_type_playback, pDevice);
24342 if (result != MA_SUCCESS) {
24343 if (pConfig->deviceType == ma_device_type_duplex) {
24344 close(pDevice->audio4.fdCapture);
24345 }
24346 return result;
24347 }
24348 }
24349
24350 return MA_SUCCESS;
24351}
24352
24353#if 0
24354static ma_result ma_device_start__audio4(ma_device* pDevice)
24355{
24356 MA_ASSERT(pDevice != NULL);
24357
24358 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24359 if (pDevice->audio4.fdCapture == -1) {
24360 return MA_INVALID_ARGS;
24361 }
24362 }
24363
24364 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24365 if (pDevice->audio4.fdPlayback == -1) {
24366 return MA_INVALID_ARGS;
24367 }
24368 }
24369
24370 return MA_SUCCESS;
24371}
24372#endif
24373
24374static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd)
24375{
24376 if (fd == -1) {
24377 return MA_INVALID_ARGS;
24378 }
24379
24380#if !defined(MA_AUDIO4_USE_NEW_API)
24381 if (ioctl(fd, AUDIO_FLUSH, 0) < 0) {
24382 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
24383 }
24384#else
24385 if (ioctl(fd, AUDIO_STOP, 0) < 0) {
24386 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
24387 }
24388#endif
24389
24390 return MA_SUCCESS;
24391}
24392
24393static ma_result ma_device_stop__audio4(ma_device* pDevice)
24394{
24395 MA_ASSERT(pDevice != NULL);
24396
24397 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24398 ma_result result;
24399
24400 result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture);
24401 if (result != MA_SUCCESS) {
24402 return result;
24403 }
24404 }
24405
24406 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24407 ma_result result;
24408
24409 /* 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. */
24410 #if !defined(MA_AUDIO4_USE_NEW_API)
24411 ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0);
24412 #endif
24413
24414 /* Here is where the device is stopped immediately. */
24415 result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback);
24416 if (result != MA_SUCCESS) {
24417 return result;
24418 }
24419 }
24420
24421 return MA_SUCCESS;
24422}
24423
24424static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
24425{
24426 int result;
24427
24428 if (pFramesWritten != NULL) {
24429 *pFramesWritten = 0;
24430 }
24431
24432 result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
24433 if (result < 0) {
24434 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
24435 }
24436
24437 if (pFramesWritten != NULL) {
24438 *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
24439 }
24440
24441 return MA_SUCCESS;
24442}
24443
24444static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
24445{
24446 int result;
24447
24448 if (pFramesRead != NULL) {
24449 *pFramesRead = 0;
24450 }
24451
24452 result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
24453 if (result < 0) {
24454 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device.", MA_FAILED_TO_READ_DATA_FROM_DEVICE);
24455 }
24456
24457 if (pFramesRead != NULL) {
24458 *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
24459 }
24460
24461 return MA_SUCCESS;
24462}
24463
24464static ma_result ma_device_main_loop__audio4(ma_device* pDevice)
24465{
24466 ma_result result = MA_SUCCESS;
24467 ma_bool32 exitLoop = MA_FALSE;
24468
24469 /* No need to explicitly start the device like the other backends. */
24470
24471 while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
24472 switch (pDevice->type)
24473 {
24474 case ma_device_type_duplex:
24475 {
24476 /* The process is: device_read -> convert -> callback -> convert -> device_write */
24477 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
24478 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
24479
24480 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
24481 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
24482 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
24483 ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
24484 ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
24485 ma_uint32 capturedDeviceFramesRemaining;
24486 ma_uint32 capturedDeviceFramesProcessed;
24487 ma_uint32 capturedDeviceFramesToProcess;
24488 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
24489 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
24490 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
24491 }
24492
24493 result = ma_device_read__audio4(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
24494 if (result != MA_SUCCESS) {
24495 exitLoop = MA_TRUE;
24496 break;
24497 }
24498
24499 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
24500 capturedDeviceFramesProcessed = 0;
24501
24502 for (;;) {
24503 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
24504 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
24505 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
24506 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
24507 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
24508 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
24509 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
24510
24511 /* Convert capture data from device format to client format. */
24512 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
24513 if (result != MA_SUCCESS) {
24514 break;
24515 }
24516
24517 /*
24518 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
24519 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
24520 */
24521 if (capturedClientFramesToProcessThisIteration == 0) {
24522 break;
24523 }
24524
24525 ma_device__on_data(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
24526
24527 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
24528 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
24529
24530 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
24531 for (;;) {
24532 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
24533 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
24534 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
24535 if (result != MA_SUCCESS) {
24536 break;
24537 }
24538
24539 result = ma_device_write__audio4(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
24540 if (result != MA_SUCCESS) {
24541 exitLoop = MA_TRUE;
24542 break;
24543 }
24544
24545 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
24546 if (capturedClientFramesToProcessThisIteration == 0) {
24547 break;
24548 }
24549 }
24550
24551 /* In case an error happened from ma_device_write__audio4()... */
24552 if (result != MA_SUCCESS) {
24553 exitLoop = MA_TRUE;
24554 break;
24555 }
24556 }
24557
24558 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
24559 }
24560 } break;
24561
24562 case ma_device_type_capture:
24563 {
24564 /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
24565 ma_uint8 intermediaryBuffer[8192];
24566 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
24567 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
24568 ma_uint32 framesReadThisPeriod = 0;
24569 while (framesReadThisPeriod < periodSizeInFrames) {
24570 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
24571 ma_uint32 framesProcessed;
24572 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
24573 if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
24574 framesToReadThisIteration = intermediaryBufferSizeInFrames;
24575 }
24576
24577 result = ma_device_read__audio4(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
24578 if (result != MA_SUCCESS) {
24579 exitLoop = MA_TRUE;
24580 break;
24581 }
24582
24583 ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
24584
24585 framesReadThisPeriod += framesProcessed;
24586 }
24587 } break;
24588
24589 case ma_device_type_playback:
24590 {
24591 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
24592 ma_uint8 intermediaryBuffer[8192];
24593 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
24594 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
24595 ma_uint32 framesWrittenThisPeriod = 0;
24596 while (framesWrittenThisPeriod < periodSizeInFrames) {
24597 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
24598 ma_uint32 framesProcessed;
24599 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
24600 if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
24601 framesToWriteThisIteration = intermediaryBufferSizeInFrames;
24602 }
24603
24604 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
24605
24606 result = ma_device_write__audio4(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
24607 if (result != MA_SUCCESS) {
24608 exitLoop = MA_TRUE;
24609 break;
24610 }
24611
24612 framesWrittenThisPeriod += framesProcessed;
24613 }
24614 } break;
24615
24616 /* To silence a warning. Will never hit this. */
24617 case ma_device_type_loopback:
24618 default: break;
24619 }
24620 }
24621
24622
24623 /* Here is where the device is stopped. */
24624 ma_device_stop__audio4(pDevice);
24625
24626 return result;
24627}
24628
24629static ma_result ma_context_uninit__audio4(ma_context* pContext)
24630{
24631 MA_ASSERT(pContext != NULL);
24632 MA_ASSERT(pContext->backend == ma_backend_audio4);
24633
24634 (void)pContext;
24635 return MA_SUCCESS;
24636}
24637
24638static ma_result ma_context_init__audio4(const ma_context_config* pConfig, ma_context* pContext)
24639{
24640 MA_ASSERT(pContext != NULL);
24641
24642 (void)pConfig;
24643
24644 pContext->onUninit = ma_context_uninit__audio4;
24645 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__audio4;
24646 pContext->onEnumDevices = ma_context_enumerate_devices__audio4;
24647 pContext->onGetDeviceInfo = ma_context_get_device_info__audio4;
24648 pContext->onDeviceInit = ma_device_init__audio4;
24649 pContext->onDeviceUninit = ma_device_uninit__audio4;
24650 pContext->onDeviceStart = NULL; /* Not required for synchronous backends. */
24651 pContext->onDeviceStop = NULL; /* Not required for synchronous backends. */
24652 pContext->onDeviceMainLoop = ma_device_main_loop__audio4;
24653
24654 return MA_SUCCESS;
24655}
24656#endif /* audio4 */
24657
24658
24659/******************************************************************************
24660
24661OSS Backend
24662
24663******************************************************************************/
24664#ifdef MA_HAS_OSS
24665#include <sys/ioctl.h>
24666#include <unistd.h>
24667#include <fcntl.h>
24668#include <sys/soundcard.h>
24669
24670#ifndef SNDCTL_DSP_HALT
24671#define SNDCTL_DSP_HALT SNDCTL_DSP_RESET
24672#endif
24673
24674static int ma_open_temp_device__oss()
24675{
24676 /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */
24677 int fd = open("/dev/mixer", O_RDONLY, 0);
24678 if (fd >= 0) {
24679 return fd;
24680 }
24681
24682 return -1;
24683}
24684
24685static 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)
24686{
24687 const char* deviceName;
24688 int flags;
24689
24690 MA_ASSERT(pContext != NULL);
24691 MA_ASSERT(pfd != NULL);
24692 (void)pContext;
24693
24694 *pfd = -1;
24695
24696 /* This function should only be called for playback or capture, not duplex. */
24697 if (deviceType == ma_device_type_duplex) {
24698 return MA_INVALID_ARGS;
24699 }
24700
24701 deviceName = "/dev/dsp";
24702 if (pDeviceID != NULL) {
24703 deviceName = pDeviceID->oss;
24704 }
24705
24706 flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY;
24707 if (shareMode == ma_share_mode_exclusive) {
24708 flags |= O_EXCL;
24709 }
24710
24711 *pfd = open(deviceName, flags, 0);
24712 if (*pfd == -1) {
24713 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
24714 }
24715
24716 return MA_SUCCESS;
24717}
24718
24719static ma_bool32 ma_context_is_device_id_equal__oss(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
24720{
24721 MA_ASSERT(pContext != NULL);
24722 MA_ASSERT(pID0 != NULL);
24723 MA_ASSERT(pID1 != NULL);
24724 (void)pContext;
24725
24726 return ma_strcmp(pID0->oss, pID1->oss) == 0;
24727}
24728
24729static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
24730{
24731 int fd;
24732 oss_sysinfo si;
24733 int result;
24734
24735 MA_ASSERT(pContext != NULL);
24736 MA_ASSERT(callback != NULL);
24737
24738 fd = ma_open_temp_device__oss();
24739 if (fd == -1) {
24740 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);
24741 }
24742
24743 result = ioctl(fd, SNDCTL_SYSINFO, &si);
24744 if (result != -1) {
24745 int iAudioDevice;
24746 for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
24747 oss_audioinfo ai;
24748 ai.dev = iAudioDevice;
24749 result = ioctl(fd, SNDCTL_AUDIOINFO, &ai);
24750 if (result != -1) {
24751 if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */
24752 ma_device_info deviceInfo;
24753 ma_bool32 isTerminating = MA_FALSE;
24754
24755 MA_ZERO_OBJECT(&deviceInfo);
24756
24757 /* ID */
24758 ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1);
24759
24760 /*
24761 The human readable device name should be in the "ai.handle" variable, but it can
24762 sometimes be empty in which case we just fall back to "ai.name" which is less user
24763 friendly, but usually has a value.
24764 */
24765 if (ai.handle[0] != '\0') {
24766 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1);
24767 } else {
24768 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1);
24769 }
24770
24771 /* The device can be both playback and capture. */
24772 if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) {
24773 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
24774 }
24775 if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) {
24776 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
24777 }
24778
24779 if (isTerminating) {
24780 break;
24781 }
24782 }
24783 }
24784 }
24785 } else {
24786 close(fd);
24787 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MA_NO_BACKEND);
24788 }
24789
24790 close(fd);
24791 return MA_SUCCESS;
24792}
24793
24794static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
24795{
24796 ma_bool32 foundDevice;
24797 int fdTemp;
24798 oss_sysinfo si;
24799 int result;
24800
24801 MA_ASSERT(pContext != NULL);
24802 (void)shareMode;
24803
24804 /* Handle the default device a little differently. */
24805 if (pDeviceID == NULL) {
24806 if (deviceType == ma_device_type_playback) {
24807 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
24808 } else {
24809 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
24810 }
24811
24812 return MA_SUCCESS;
24813 }
24814
24815
24816 /* If we get here it means we are _not_ using the default device. */
24817 foundDevice = MA_FALSE;
24818
24819 fdTemp = ma_open_temp_device__oss();
24820 if (fdTemp == -1) {
24821 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);
24822 }
24823
24824 result = ioctl(fdTemp, SNDCTL_SYSINFO, &si);
24825 if (result != -1) {
24826 int iAudioDevice;
24827 for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
24828 oss_audioinfo ai;
24829 ai.dev = iAudioDevice;
24830 result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai);
24831 if (result != -1) {
24832 if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) {
24833 /* It has the same name, so now just confirm the type. */
24834 if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) ||
24835 (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) {
24836 unsigned int formatMask;
24837
24838 /* ID */
24839 ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1);
24840
24841 /*
24842 The human readable device name should be in the "ai.handle" variable, but it can
24843 sometimes be empty in which case we just fall back to "ai.name" which is less user
24844 friendly, but usually has a value.
24845 */
24846 if (ai.handle[0] != '\0') {
24847 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1);
24848 } else {
24849 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1);
24850 }
24851
24852 pDeviceInfo->minChannels = ai.min_channels;
24853 pDeviceInfo->maxChannels = ai.max_channels;
24854 pDeviceInfo->minSampleRate = ai.min_rate;
24855 pDeviceInfo->maxSampleRate = ai.max_rate;
24856 pDeviceInfo->formatCount = 0;
24857
24858 if (deviceType == ma_device_type_playback) {
24859 formatMask = ai.oformats;
24860 } else {
24861 formatMask = ai.iformats;
24862 }
24863
24864 if ((formatMask & AFMT_U8) != 0) {
24865 pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_u8;
24866 }
24867 if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) {
24868 pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s16;
24869 }
24870 if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) {
24871 pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s32;
24872 }
24873
24874 foundDevice = MA_TRUE;
24875 break;
24876 }
24877 }
24878 }
24879 }
24880 } else {
24881 close(fdTemp);
24882 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MA_NO_BACKEND);
24883 }
24884
24885
24886 close(fdTemp);
24887
24888 if (!foundDevice) {
24889 return MA_NO_DEVICE;
24890 }
24891
24892 return MA_SUCCESS;
24893}
24894
24895static void ma_device_uninit__oss(ma_device* pDevice)
24896{
24897 MA_ASSERT(pDevice != NULL);
24898
24899 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24900 close(pDevice->oss.fdCapture);
24901 }
24902
24903 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24904 close(pDevice->oss.fdPlayback);
24905 }
24906}
24907
24908static int ma_format_to_oss(ma_format format)
24909{
24910 int ossFormat = AFMT_U8;
24911 switch (format) {
24912 case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
24913 case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
24914 case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
24915 case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
24916 case ma_format_u8:
24917 default: ossFormat = AFMT_U8; break;
24918 }
24919
24920 return ossFormat;
24921}
24922
24923static ma_format ma_format_from_oss(int ossFormat)
24924{
24925 if (ossFormat == AFMT_U8) {
24926 return ma_format_u8;
24927 } else {
24928 if (ma_is_little_endian()) {
24929 switch (ossFormat) {
24930 case AFMT_S16_LE: return ma_format_s16;
24931 case AFMT_S32_LE: return ma_format_s32;
24932 default: return ma_format_unknown;
24933 }
24934 } else {
24935 switch (ossFormat) {
24936 case AFMT_S16_BE: return ma_format_s16;
24937 case AFMT_S32_BE: return ma_format_s32;
24938 default: return ma_format_unknown;
24939 }
24940 }
24941 }
24942
24943 return ma_format_unknown;
24944}
24945
24946static ma_result ma_device_init_fd__oss(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
24947{
24948 ma_result result;
24949 int ossResult;
24950 int fd;
24951 const ma_device_id* pDeviceID = NULL;
24952 ma_share_mode shareMode;
24953 int ossFormat;
24954 int ossChannels;
24955 int ossSampleRate;
24956 int ossFragment;
24957
24958 MA_ASSERT(pContext != NULL);
24959 MA_ASSERT(pConfig != NULL);
24960 MA_ASSERT(deviceType != ma_device_type_duplex);
24961 MA_ASSERT(pDevice != NULL);
24962
24963 (void)pContext;
24964
24965 if (deviceType == ma_device_type_capture) {
24966 pDeviceID = pConfig->capture.pDeviceID;
24967 shareMode = pConfig->capture.shareMode;
24968 ossFormat = ma_format_to_oss(pConfig->capture.format);
24969 ossChannels = (int)pConfig->capture.channels;
24970 ossSampleRate = (int)pConfig->sampleRate;
24971 } else {
24972 pDeviceID = pConfig->playback.pDeviceID;
24973 shareMode = pConfig->playback.shareMode;
24974 ossFormat = ma_format_to_oss(pConfig->playback.format);
24975 ossChannels = (int)pConfig->playback.channels;
24976 ossSampleRate = (int)pConfig->sampleRate;
24977 }
24978
24979 result = ma_context_open_device__oss(pContext, deviceType, pDeviceID, shareMode, &fd);
24980 if (result != MA_SUCCESS) {
24981 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24982 }
24983
24984 /*
24985 The OSS documantation is very clear about the order we should be initializing the device's properties:
24986 1) Format
24987 2) Channels
24988 3) Sample rate.
24989 */
24990
24991 /* Format. */
24992 ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat);
24993 if (ossResult == -1) {
24994 close(fd);
24995 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format.", MA_FORMAT_NOT_SUPPORTED);
24996 }
24997
24998 /* Channels. */
24999 ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels);
25000 if (ossResult == -1) {
25001 close(fd);
25002 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count.", MA_FORMAT_NOT_SUPPORTED);
25003 }
25004
25005 /* Sample Rate. */
25006 ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate);
25007 if (ossResult == -1) {
25008 close(fd);
25009 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate.", MA_FORMAT_NOT_SUPPORTED);
25010 }
25011
25012 /*
25013 Buffer.
25014
25015 The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if
25016 it should be done before or after format/channels/rate.
25017
25018 OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual
25019 value.
25020 */
25021 {
25022 ma_uint32 periodSizeInFrames;
25023 ma_uint32 periodSizeInBytes;
25024 ma_uint32 ossFragmentSizePower;
25025
25026 periodSizeInFrames = pConfig->periodSizeInFrames;
25027 if (periodSizeInFrames == 0) {
25028 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, (ma_uint32)ossSampleRate);
25029 }
25030
25031 periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels));
25032 if (periodSizeInBytes < 16) {
25033 periodSizeInBytes = 16;
25034 }
25035
25036 ossFragmentSizePower = 4;
25037 periodSizeInBytes >>= 4;
25038 while (periodSizeInBytes >>= 1) {
25039 ossFragmentSizePower += 1;
25040 }
25041
25042 ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower);
25043 ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment);
25044 if (ossResult == -1) {
25045 close(fd);
25046 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count.", MA_FORMAT_NOT_SUPPORTED);
25047 }
25048 }
25049
25050 /* Internal settings. */
25051 if (deviceType == ma_device_type_capture) {
25052 pDevice->oss.fdCapture = fd;
25053 pDevice->capture.internalFormat = ma_format_from_oss(ossFormat);
25054 pDevice->capture.internalChannels = ossChannels;
25055 pDevice->capture.internalSampleRate = ossSampleRate;
25056 ma_get_standard_channel_map(ma_standard_channel_map_sound4, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
25057 pDevice->capture.internalPeriods = (ma_uint32)(ossFragment >> 16);
25058 pDevice->capture.internalPeriodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
25059
25060 if (pDevice->capture.internalFormat == ma_format_unknown) {
25061 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
25062 }
25063 } else {
25064 pDevice->oss.fdPlayback = fd;
25065 pDevice->playback.internalFormat = ma_format_from_oss(ossFormat);
25066 pDevice->playback.internalChannels = ossChannels;
25067 pDevice->playback.internalSampleRate = ossSampleRate;
25068 ma_get_standard_channel_map(ma_standard_channel_map_sound4, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
25069 pDevice->playback.internalPeriods = (ma_uint32)(ossFragment >> 16);
25070 pDevice->playback.internalPeriodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
25071
25072 if (pDevice->playback.internalFormat == ma_format_unknown) {
25073 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
25074 }
25075 }
25076
25077 return MA_SUCCESS;
25078}
25079
25080static ma_result ma_device_init__oss(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
25081{
25082 MA_ASSERT(pContext != NULL);
25083 MA_ASSERT(pConfig != NULL);
25084 MA_ASSERT(pDevice != NULL);
25085
25086 MA_ZERO_OBJECT(&pDevice->oss);
25087
25088 if (pConfig->deviceType == ma_device_type_loopback) {
25089 return MA_DEVICE_TYPE_NOT_SUPPORTED;
25090 }
25091
25092 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
25093 ma_result result = ma_device_init_fd__oss(pContext, pConfig, ma_device_type_capture, pDevice);
25094 if (result != MA_SUCCESS) {
25095 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
25096 }
25097 }
25098
25099 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
25100 ma_result result = ma_device_init_fd__oss(pContext, pConfig, ma_device_type_playback, pDevice);
25101 if (result != MA_SUCCESS) {
25102 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
25103 }
25104 }
25105
25106 return MA_SUCCESS;
25107}
25108
25109static ma_result ma_device_stop__oss(ma_device* pDevice)
25110{
25111 MA_ASSERT(pDevice != NULL);
25112
25113 /*
25114 We want to use SNDCTL_DSP_HALT. From the documentation:
25115
25116 In multithreaded applications SNDCTL_DSP_HALT (SNDCTL_DSP_RESET) must only be called by the thread
25117 that actually reads/writes the audio device. It must not be called by some master thread to kill the
25118 audio thread. The audio thread will not stop or get any kind of notification that the device was
25119 stopped by the master thread. The device gets stopped but the next read or write call will silently
25120 restart the device.
25121
25122 This is actually safe in our case, because this function is only ever called from within our worker
25123 thread anyway. Just keep this in mind, though...
25124 */
25125
25126 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
25127 int result = ioctl(pDevice->oss.fdCapture, SNDCTL_DSP_HALT, 0);
25128 if (result == -1) {
25129 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to stop device. SNDCTL_DSP_HALT failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
25130 }
25131 }
25132
25133 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
25134 int result = ioctl(pDevice->oss.fdPlayback, SNDCTL_DSP_HALT, 0);
25135 if (result == -1) {
25136 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to stop device. SNDCTL_DSP_HALT failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
25137 }
25138 }
25139
25140 return MA_SUCCESS;
25141}
25142
25143static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
25144{
25145 int resultOSS;
25146
25147 if (pFramesWritten != NULL) {
25148 *pFramesWritten = 0;
25149 }
25150
25151 resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
25152 if (resultOSS < 0) {
25153 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
25154 }
25155
25156 if (pFramesWritten != NULL) {
25157 *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
25158 }
25159
25160 return MA_SUCCESS;
25161}
25162
25163static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
25164{
25165 int resultOSS;
25166
25167 if (pFramesRead != NULL) {
25168 *pFramesRead = 0;
25169 }
25170
25171 resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
25172 if (resultOSS < 0) {
25173 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client.", MA_FAILED_TO_READ_DATA_FROM_DEVICE);
25174 }
25175
25176 if (pFramesRead != NULL) {
25177 *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
25178 }
25179
25180 return MA_SUCCESS;
25181}
25182
25183static ma_result ma_device_main_loop__oss(ma_device* pDevice)
25184{
25185 ma_result result = MA_SUCCESS;
25186 ma_bool32 exitLoop = MA_FALSE;
25187
25188 /* No need to explicitly start the device like the other backends. */
25189
25190 while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
25191 switch (pDevice->type)
25192 {
25193 case ma_device_type_duplex:
25194 {
25195 /* The process is: device_read -> convert -> callback -> convert -> device_write */
25196 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
25197 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
25198
25199 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
25200 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
25201 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
25202 ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
25203 ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
25204 ma_uint32 capturedDeviceFramesRemaining;
25205 ma_uint32 capturedDeviceFramesProcessed;
25206 ma_uint32 capturedDeviceFramesToProcess;
25207 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
25208 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
25209 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
25210 }
25211
25212 result = ma_device_read__oss(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
25213 if (result != MA_SUCCESS) {
25214 exitLoop = MA_TRUE;
25215 break;
25216 }
25217
25218 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
25219 capturedDeviceFramesProcessed = 0;
25220
25221 for (;;) {
25222 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
25223 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
25224 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
25225 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
25226 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
25227 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
25228 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
25229
25230 /* Convert capture data from device format to client format. */
25231 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
25232 if (result != MA_SUCCESS) {
25233 break;
25234 }
25235
25236 /*
25237 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
25238 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
25239 */
25240 if (capturedClientFramesToProcessThisIteration == 0) {
25241 break;
25242 }
25243
25244 ma_device__on_data(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
25245
25246 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
25247 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
25248
25249 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
25250 for (;;) {
25251 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
25252 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
25253 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
25254 if (result != MA_SUCCESS) {
25255 break;
25256 }
25257
25258 result = ma_device_write__oss(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
25259 if (result != MA_SUCCESS) {
25260 exitLoop = MA_TRUE;
25261 break;
25262 }
25263
25264 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
25265 if (capturedClientFramesToProcessThisIteration == 0) {
25266 break;
25267 }
25268 }
25269
25270 /* In case an error happened from ma_device_write__oss()... */
25271 if (result != MA_SUCCESS) {
25272 exitLoop = MA_TRUE;
25273 break;
25274 }
25275 }
25276
25277 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
25278 }
25279 } break;
25280
25281 case ma_device_type_capture:
25282 {
25283 /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
25284 ma_uint8 intermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
25285 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
25286 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
25287 ma_uint32 framesReadThisPeriod = 0;
25288 while (framesReadThisPeriod < periodSizeInFrames) {
25289 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
25290 ma_uint32 framesProcessed;
25291 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
25292 if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
25293 framesToReadThisIteration = intermediaryBufferSizeInFrames;
25294 }
25295
25296 result = ma_device_read__oss(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
25297 if (result != MA_SUCCESS) {
25298 exitLoop = MA_TRUE;
25299 break;
25300 }
25301
25302 ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
25303
25304 framesReadThisPeriod += framesProcessed;
25305 }
25306 } break;
25307
25308 case ma_device_type_playback:
25309 {
25310 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
25311 ma_uint8 intermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
25312 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
25313 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
25314 ma_uint32 framesWrittenThisPeriod = 0;
25315 while (framesWrittenThisPeriod < periodSizeInFrames) {
25316 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
25317 ma_uint32 framesProcessed;
25318 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
25319 if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
25320 framesToWriteThisIteration = intermediaryBufferSizeInFrames;
25321 }
25322
25323 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
25324
25325 result = ma_device_write__oss(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
25326 if (result != MA_SUCCESS) {
25327 exitLoop = MA_TRUE;
25328 break;
25329 }
25330
25331 framesWrittenThisPeriod += framesProcessed;
25332 }
25333 } break;
25334
25335 /* To silence a warning. Will never hit this. */
25336 case ma_device_type_loopback:
25337 default: break;
25338 }
25339 }
25340
25341
25342 /* Here is where the device is stopped. */
25343 ma_device_stop__oss(pDevice);
25344
25345 return result;
25346}
25347
25348static ma_result ma_context_uninit__oss(ma_context* pContext)
25349{
25350 MA_ASSERT(pContext != NULL);
25351 MA_ASSERT(pContext->backend == ma_backend_oss);
25352
25353 (void)pContext;
25354 return MA_SUCCESS;
25355}
25356
25357static ma_result ma_context_init__oss(const ma_context_config* pConfig, ma_context* pContext)
25358{
25359 int fd;
25360 int ossVersion;
25361 int result;
25362
25363 MA_ASSERT(pContext != NULL);
25364
25365 (void)pConfig;
25366
25367 /* Try opening a temporary device first so we can get version information. This is closed at the end. */
25368 fd = ma_open_temp_device__oss();
25369 if (fd == -1) {
25370 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. */
25371 }
25372
25373 /* Grab the OSS version. */
25374 ossVersion = 0;
25375 result = ioctl(fd, OSS_GETVERSION, &ossVersion);
25376 if (result == -1) {
25377 close(fd);
25378 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version.", MA_NO_BACKEND);
25379 }
25380
25381 pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16);
25382 pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8);
25383
25384 pContext->onUninit = ma_context_uninit__oss;
25385 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__oss;
25386 pContext->onEnumDevices = ma_context_enumerate_devices__oss;
25387 pContext->onGetDeviceInfo = ma_context_get_device_info__oss;
25388 pContext->onDeviceInit = ma_device_init__oss;
25389 pContext->onDeviceUninit = ma_device_uninit__oss;
25390 pContext->onDeviceStart = NULL; /* Not required for synchronous backends. */
25391 pContext->onDeviceStop = NULL; /* Not required for synchronous backends. */
25392 pContext->onDeviceMainLoop = ma_device_main_loop__oss;
25393
25394 close(fd);
25395 return MA_SUCCESS;
25396}
25397#endif /* OSS */
25398
25399
25400/******************************************************************************
25401
25402AAudio Backend
25403
25404******************************************************************************/
25405#ifdef MA_HAS_AAUDIO
25406/*#include <AAudio/AAudio.h>*/
25407
25408#define MA_AAUDIO_UNSPECIFIED 0
25409
25410typedef int32_t ma_aaudio_result_t;
25411typedef int32_t ma_aaudio_direction_t;
25412typedef int32_t ma_aaudio_sharing_mode_t;
25413typedef int32_t ma_aaudio_format_t;
25414typedef int32_t ma_aaudio_stream_state_t;
25415typedef int32_t ma_aaudio_performance_mode_t;
25416typedef int32_t ma_aaudio_data_callback_result_t;
25417
25418/* Result codes. miniaudio only cares about the success code. */
25419#define MA_AAUDIO_OK 0
25420
25421/* Directions. */
25422#define MA_AAUDIO_DIRECTION_OUTPUT 0
25423#define MA_AAUDIO_DIRECTION_INPUT 1
25424
25425/* Sharing modes. */
25426#define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0
25427#define MA_AAUDIO_SHARING_MODE_SHARED 1
25428
25429/* Formats. */
25430#define MA_AAUDIO_FORMAT_PCM_I16 1
25431#define MA_AAUDIO_FORMAT_PCM_FLOAT 2
25432
25433/* Stream states. */
25434#define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0
25435#define MA_AAUDIO_STREAM_STATE_UNKNOWN 1
25436#define MA_AAUDIO_STREAM_STATE_OPEN 2
25437#define MA_AAUDIO_STREAM_STATE_STARTING 3
25438#define MA_AAUDIO_STREAM_STATE_STARTED 4
25439#define MA_AAUDIO_STREAM_STATE_PAUSING 5
25440#define MA_AAUDIO_STREAM_STATE_PAUSED 6
25441#define MA_AAUDIO_STREAM_STATE_FLUSHING 7
25442#define MA_AAUDIO_STREAM_STATE_FLUSHED 8
25443#define MA_AAUDIO_STREAM_STATE_STOPPING 9
25444#define MA_AAUDIO_STREAM_STATE_STOPPED 10
25445#define MA_AAUDIO_STREAM_STATE_CLOSING 11
25446#define MA_AAUDIO_STREAM_STATE_CLOSED 12
25447#define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13
25448
25449/* Performance modes. */
25450#define MA_AAUDIO_PERFORMANCE_MODE_NONE 10
25451#define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11
25452#define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12
25453
25454/* Callback results. */
25455#define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0
25456#define MA_AAUDIO_CALLBACK_RESULT_STOP 1
25457
25458/* Objects. */
25459typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder;
25460typedef struct ma_AAudioStream_t* ma_AAudioStream;
25461
25462typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames);
25463typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error);
25464
25465typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder);
25466typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder);
25467typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId);
25468typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction);
25469typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode);
25470typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format);
25471typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount);
25472typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate);
25473typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
25474typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
25475typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData);
25476typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData);
25477typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode);
25478typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream);
25479typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream);
25480typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream);
25481typedef 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);
25482typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream);
25483typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream);
25484typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream);
25485typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream);
25486typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream);
25487typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream);
25488typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream);
25489typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream);
25490
25491static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA)
25492{
25493 switch (resultAA)
25494 {
25495 case MA_AAUDIO_OK: return MA_SUCCESS;
25496 default: break;
25497 }
25498
25499 return MA_ERROR;
25500}
25501
25502static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error)
25503{
25504 ma_device* pDevice = (ma_device*)pUserData;
25505 MA_ASSERT(pDevice != NULL);
25506
25507 (void)error;
25508
25509#if defined(MA_DEBUG_OUTPUT)
25510 printf("[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream));
25511#endif
25512
25513 /*
25514 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
25515 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.
25516 */
25517 if (((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream) == MA_AAUDIO_STREAM_STATE_DISCONNECTED) {
25518#if defined(MA_DEBUG_OUTPUT)
25519 printf("[AAudio] Device Disconnected.\n");
25520#endif
25521 }
25522}
25523
25524static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
25525{
25526 ma_device* pDevice = (ma_device*)pUserData;
25527 MA_ASSERT(pDevice != NULL);
25528
25529 if (pDevice->type == ma_device_type_duplex) {
25530 ma_device__handle_duplex_callback_capture(pDevice, frameCount, pAudioData, &pDevice->aaudio.duplexRB);
25531 } else {
25532 ma_device__send_frames_to_client(pDevice, frameCount, pAudioData); /* Send directly to the client. */
25533 }
25534
25535 (void)pStream;
25536 return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
25537}
25538
25539static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
25540{
25541 ma_device* pDevice = (ma_device*)pUserData;
25542 MA_ASSERT(pDevice != NULL);
25543
25544 if (pDevice->type == ma_device_type_duplex) {
25545 ma_device__handle_duplex_callback_playback(pDevice, frameCount, pAudioData, &pDevice->aaudio.duplexRB);
25546 } else {
25547 ma_device__read_frames_from_client(pDevice, frameCount, pAudioData); /* Read directly from the client. */
25548 }
25549
25550 (void)pStream;
25551 return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
25552}
25553
25554static ma_result ma_open_stream__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, const ma_device_config* pConfig, const ma_device* pDevice, ma_AAudioStream** ppStream)
25555{
25556 ma_AAudioStreamBuilder* pBuilder;
25557 ma_aaudio_result_t resultAA;
25558
25559 MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */
25560
25561 *ppStream = NULL;
25562
25563 resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder);
25564 if (resultAA != MA_AAUDIO_OK) {
25565 return ma_result_from_aaudio(resultAA);
25566 }
25567
25568 if (pDeviceID != NULL) {
25569 ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio);
25570 }
25571
25572 ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT);
25573 ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE);
25574
25575 if (pConfig != NULL) {
25576 ma_uint32 bufferCapacityInFrames;
25577
25578 if (pDevice == NULL || !pDevice->usingDefaultSampleRate) {
25579 ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pConfig->sampleRate);
25580 }
25581
25582 if (deviceType == ma_device_type_capture) {
25583 if (pDevice == NULL || !pDevice->capture.usingDefaultChannels) {
25584 ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pConfig->capture.channels);
25585 }
25586 if (pDevice == NULL || !pDevice->capture.usingDefaultFormat) {
25587 ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pConfig->capture.format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
25588 }
25589 } else {
25590 if (pDevice == NULL || !pDevice->playback.usingDefaultChannels) {
25591 ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pConfig->playback.channels);
25592 }
25593 if (pDevice == NULL || !pDevice->playback.usingDefaultFormat) {
25594 ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pConfig->playback.format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
25595 }
25596 }
25597
25598 bufferCapacityInFrames = pConfig->periodSizeInFrames * pConfig->periods;
25599 if (bufferCapacityInFrames == 0) {
25600 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pConfig->sampleRate) * pConfig->periods;
25601 }
25602
25603 ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames);
25604 ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pConfig->periods);
25605
25606 if (deviceType == ma_device_type_capture) {
25607 ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice);
25608 } else {
25609 ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice);
25610 }
25611
25612 /* 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. */
25613 ((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);
25614 }
25615
25616 ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice);
25617
25618 resultAA = ((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream);
25619 if (resultAA != MA_AAUDIO_OK) {
25620 *ppStream = NULL;
25621 ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
25622 return ma_result_from_aaudio(resultAA);
25623 }
25624
25625 ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
25626 return MA_SUCCESS;
25627}
25628
25629static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream)
25630{
25631 return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream));
25632}
25633
25634static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType)
25635{
25636 /* The only way to know this is to try creating a stream. */
25637 ma_AAudioStream* pStream;
25638 ma_result result = ma_open_stream__aaudio(pContext, deviceType, NULL, ma_share_mode_shared, NULL, NULL, &pStream);
25639 if (result != MA_SUCCESS) {
25640 return MA_FALSE;
25641 }
25642
25643 ma_close_stream__aaudio(pContext, pStream);
25644 return MA_TRUE;
25645}
25646
25647static 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)
25648{
25649 ma_aaudio_stream_state_t actualNewState;
25650 ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */
25651 if (resultAA != MA_AAUDIO_OK) {
25652 return ma_result_from_aaudio(resultAA);
25653 }
25654
25655 if (newState != actualNewState) {
25656 return MA_ERROR; /* Failed to transition into the expected state. */
25657 }
25658
25659 return MA_SUCCESS;
25660}
25661
25662
25663static ma_bool32 ma_context_is_device_id_equal__aaudio(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
25664{
25665 MA_ASSERT(pContext != NULL);
25666 MA_ASSERT(pID0 != NULL);
25667 MA_ASSERT(pID1 != NULL);
25668 (void)pContext;
25669
25670 return pID0->aaudio == pID1->aaudio;
25671}
25672
25673static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
25674{
25675 ma_bool32 cbResult = MA_TRUE;
25676
25677 MA_ASSERT(pContext != NULL);
25678 MA_ASSERT(callback != NULL);
25679
25680 /* 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. */
25681
25682 /* Playback. */
25683 if (cbResult) {
25684 ma_device_info deviceInfo;
25685 MA_ZERO_OBJECT(&deviceInfo);
25686 deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
25687 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
25688
25689 if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) {
25690 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
25691 }
25692 }
25693
25694 /* Capture. */
25695 if (cbResult) {
25696 ma_device_info deviceInfo;
25697 MA_ZERO_OBJECT(&deviceInfo);
25698 deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
25699 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
25700
25701 if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) {
25702 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
25703 }
25704 }
25705
25706 return MA_SUCCESS;
25707}
25708
25709static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
25710{
25711 ma_AAudioStream* pStream;
25712 ma_result result;
25713
25714 MA_ASSERT(pContext != NULL);
25715
25716 /* No exclusive mode with AAudio. */
25717 if (shareMode == ma_share_mode_exclusive) {
25718 return MA_SHARE_MODE_NOT_SUPPORTED;
25719 }
25720
25721 /* ID */
25722 if (pDeviceID != NULL) {
25723 pDeviceInfo->id.aaudio = pDeviceID->aaudio;
25724 } else {
25725 pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED;
25726 }
25727
25728 /* Name */
25729 if (deviceType == ma_device_type_playback) {
25730 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
25731 } else {
25732 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
25733 }
25734
25735
25736 /* We'll need to open the device to get accurate sample rate and channel count information. */
25737 result = ma_open_stream__aaudio(pContext, deviceType, pDeviceID, shareMode, NULL, NULL, &pStream);
25738 if (result != MA_SUCCESS) {
25739 return result;
25740 }
25741
25742 pDeviceInfo->minChannels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream);
25743 pDeviceInfo->maxChannels = pDeviceInfo->minChannels;
25744 pDeviceInfo->minSampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream);
25745 pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
25746
25747 ma_close_stream__aaudio(pContext, pStream);
25748 pStream = NULL;
25749
25750
25751 /* AAudio supports s16 and f32. */
25752 pDeviceInfo->formatCount = 2;
25753 pDeviceInfo->formats[0] = ma_format_s16;
25754 pDeviceInfo->formats[1] = ma_format_f32;
25755
25756 return MA_SUCCESS;
25757}
25758
25759
25760static void ma_device_uninit__aaudio(ma_device* pDevice)
25761{
25762 MA_ASSERT(pDevice != NULL);
25763
25764 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
25765 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
25766 pDevice->aaudio.pStreamCapture = NULL;
25767 }
25768
25769 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
25770 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
25771 pDevice->aaudio.pStreamPlayback = NULL;
25772 }
25773
25774 if (pDevice->type == ma_device_type_duplex) {
25775 ma_pcm_rb_uninit(&pDevice->aaudio.duplexRB);
25776 }
25777}
25778
25779static ma_result ma_device_init__aaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
25780{
25781 ma_result result;
25782
25783 MA_ASSERT(pDevice != NULL);
25784
25785 if (pConfig->deviceType == ma_device_type_loopback) {
25786 return MA_DEVICE_TYPE_NOT_SUPPORTED;
25787 }
25788
25789 /* No exclusive mode with AAudio. */
25790 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
25791 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
25792 return MA_SHARE_MODE_NOT_SUPPORTED;
25793 }
25794
25795 /* We first need to try opening the stream. */
25796 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
25797 int32_t bufferCapacityInFrames;
25798 int32_t framesPerDataCallback;
25799
25800 result = ma_open_stream__aaudio(pContext, ma_device_type_capture, pConfig->capture.pDeviceID, pConfig->capture.shareMode, pConfig, pDevice, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture);
25801 if (result != MA_SUCCESS) {
25802 return result; /* Failed to open the AAudio stream. */
25803 }
25804
25805 pDevice->capture.internalFormat = (((MA_PFN_AAudioStream_getFormat)pContext->aaudio.AAudioStream_getFormat)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32;
25806 pDevice->capture.internalChannels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
25807 pDevice->capture.internalSampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
25808 ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap); /* <-- Cannot find info on channel order, so assuming a default. */
25809
25810 bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pContext->aaudio.AAudioStream_getBufferCapacityInFrames)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
25811 framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pContext->aaudio.AAudioStream_getFramesPerDataCallback)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
25812
25813 if (framesPerDataCallback > 0) {
25814 pDevice->capture.internalPeriodSizeInFrames = framesPerDataCallback;
25815 pDevice->capture.internalPeriods = bufferCapacityInFrames / framesPerDataCallback;
25816 } else {
25817 pDevice->capture.internalPeriodSizeInFrames = bufferCapacityInFrames;
25818 pDevice->capture.internalPeriods = 1;
25819 }
25820 }
25821
25822 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
25823 int32_t bufferCapacityInFrames;
25824 int32_t framesPerDataCallback;
25825
25826 result = ma_open_stream__aaudio(pContext, ma_device_type_playback, pConfig->playback.pDeviceID, pConfig->playback.shareMode, pConfig, pDevice, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback);
25827 if (result != MA_SUCCESS) {
25828 return result; /* Failed to open the AAudio stream. */
25829 }
25830
25831 pDevice->playback.internalFormat = (((MA_PFN_AAudioStream_getFormat)pContext->aaudio.AAudioStream_getFormat)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32;
25832 pDevice->playback.internalChannels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
25833 pDevice->playback.internalSampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
25834 ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap); /* <-- Cannot find info on channel order, so assuming a default. */
25835
25836 bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pContext->aaudio.AAudioStream_getBufferCapacityInFrames)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
25837 framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pContext->aaudio.AAudioStream_getFramesPerDataCallback)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
25838
25839 if (framesPerDataCallback > 0) {
25840 pDevice->playback.internalPeriodSizeInFrames = framesPerDataCallback;
25841 pDevice->playback.internalPeriods = bufferCapacityInFrames / framesPerDataCallback;
25842 } else {
25843 pDevice->playback.internalPeriodSizeInFrames = bufferCapacityInFrames;
25844 pDevice->playback.internalPeriods = 1;
25845 }
25846 }
25847
25848 if (pConfig->deviceType == ma_device_type_duplex) {
25849 ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames) * pDevice->capture.internalPeriods;
25850 ma_result result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->pContext->allocationCallbacks, &pDevice->aaudio.duplexRB);
25851 if (result != MA_SUCCESS) {
25852 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
25853 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
25854 }
25855 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
25856 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
25857 }
25858 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[AAudio] Failed to initialize ring buffer.", result);
25859 }
25860
25861 /* We need a period to act as a buffer for cases where the playback and capture device's end up desyncing. */
25862 {
25863 ma_uint32 marginSizeInFrames = rbSizeInFrames / pDevice->capture.internalPeriods;
25864 void* pMarginData;
25865 ma_pcm_rb_acquire_write(&pDevice->aaudio.duplexRB, &marginSizeInFrames, &pMarginData);
25866 {
25867 MA_ZERO_MEMORY(pMarginData, marginSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
25868 }
25869 ma_pcm_rb_commit_write(&pDevice->aaudio.duplexRB, marginSizeInFrames, pMarginData);
25870 }
25871 }
25872
25873 return MA_SUCCESS;
25874}
25875
25876static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
25877{
25878 ma_aaudio_result_t resultAA;
25879 ma_aaudio_stream_state_t currentState;
25880
25881 MA_ASSERT(pDevice != NULL);
25882
25883 resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream);
25884 if (resultAA != MA_AAUDIO_OK) {
25885 return ma_result_from_aaudio(resultAA);
25886 }
25887
25888 /* Do we actually need to wait for the device to transition into it's started state? */
25889
25890 /* 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. */
25891 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
25892 if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) {
25893 ma_result result;
25894
25895 if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) {
25896 return MA_ERROR; /* Expecting the stream to be a starting or started state. */
25897 }
25898
25899 result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED);
25900 if (result != MA_SUCCESS) {
25901 return result;
25902 }
25903 }
25904
25905 return MA_SUCCESS;
25906}
25907
25908static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
25909{
25910 ma_aaudio_result_t resultAA;
25911 ma_aaudio_stream_state_t currentState;
25912
25913 MA_ASSERT(pDevice != NULL);
25914
25915 /*
25916 From the AAudio documentation:
25917
25918 The stream will stop after all of the data currently buffered has been played.
25919
25920 This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic.
25921 */
25922
25923 resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream);
25924 if (resultAA != MA_AAUDIO_OK) {
25925 return ma_result_from_aaudio(resultAA);
25926 }
25927
25928 /* 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. */
25929 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
25930 if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) {
25931 ma_result result;
25932
25933 if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) {
25934 return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */
25935 }
25936
25937 result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED);
25938 if (result != MA_SUCCESS) {
25939 return result;
25940 }
25941 }
25942
25943 return MA_SUCCESS;
25944}
25945
25946static ma_result ma_device_start__aaudio(ma_device* pDevice)
25947{
25948 MA_ASSERT(pDevice != NULL);
25949
25950 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
25951 ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
25952 if (result != MA_SUCCESS) {
25953 return result;
25954 }
25955 }
25956
25957 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
25958 ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
25959 if (result != MA_SUCCESS) {
25960 if (pDevice->type == ma_device_type_duplex) {
25961 ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
25962 }
25963 return result;
25964 }
25965 }
25966
25967 return MA_SUCCESS;
25968}
25969
25970static ma_result ma_device_stop__aaudio(ma_device* pDevice)
25971{
25972 ma_stop_proc onStop;
25973
25974 MA_ASSERT(pDevice != NULL);
25975
25976 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
25977 ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
25978 if (result != MA_SUCCESS) {
25979 return result;
25980 }
25981 }
25982
25983 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
25984 ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
25985 if (result != MA_SUCCESS) {
25986 return result;
25987 }
25988 }
25989
25990 onStop = pDevice->onStop;
25991 if (onStop) {
25992 onStop(pDevice);
25993 }
25994
25995 return MA_SUCCESS;
25996}
25997
25998
25999static ma_result ma_context_uninit__aaudio(ma_context* pContext)
26000{
26001 MA_ASSERT(pContext != NULL);
26002 MA_ASSERT(pContext->backend == ma_backend_aaudio);
26003
26004 ma_dlclose(pContext, pContext->aaudio.hAAudio);
26005 pContext->aaudio.hAAudio = NULL;
26006
26007 return MA_SUCCESS;
26008}
26009
26010static ma_result ma_context_init__aaudio(const ma_context_config* pConfig, ma_context* pContext)
26011{
26012 const char* libNames[] = {
26013 "libaaudio.so"
26014 };
26015 size_t i;
26016
26017 for (i = 0; i < ma_countof(libNames); ++i) {
26018 pContext->aaudio.hAAudio = ma_dlopen(pContext, libNames[i]);
26019 if (pContext->aaudio.hAAudio != NULL) {
26020 break;
26021 }
26022 }
26023
26024 if (pContext->aaudio.hAAudio == NULL) {
26025 return MA_FAILED_TO_INIT_BACKEND;
26026 }
26027
26028 pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudio_createStreamBuilder");
26029 pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete");
26030 pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId");
26031 pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection");
26032 pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode");
26033 pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat");
26034 pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount");
26035 pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate");
26036 pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames");
26037 pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback");
26038 pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback");
26039 pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback");
26040 pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode");
26041 pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream");
26042 pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close");
26043 pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState");
26044 pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange");
26045 pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFormat");
26046 pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getChannelCount");
26047 pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getSampleRate");
26048 pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames");
26049 pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback");
26050 pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst");
26051 pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStart");
26052 pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStop");
26053
26054 pContext->isBackendAsynchronous = MA_TRUE;
26055
26056 pContext->onUninit = ma_context_uninit__aaudio;
26057 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__aaudio;
26058 pContext->onEnumDevices = ma_context_enumerate_devices__aaudio;
26059 pContext->onGetDeviceInfo = ma_context_get_device_info__aaudio;
26060 pContext->onDeviceInit = ma_device_init__aaudio;
26061 pContext->onDeviceUninit = ma_device_uninit__aaudio;
26062 pContext->onDeviceStart = ma_device_start__aaudio;
26063 pContext->onDeviceStop = ma_device_stop__aaudio;
26064
26065 (void)pConfig;
26066 return MA_SUCCESS;
26067}
26068#endif /* AAudio */
26069
26070
26071/******************************************************************************
26072
26073OpenSL|ES Backend
26074
26075******************************************************************************/
26076#ifdef MA_HAS_OPENSL
26077#include <SLES/OpenSLES.h>
26078#ifdef MA_ANDROID
26079#include <SLES/OpenSLES_Android.h>
26080#endif
26081
26082/* OpenSL|ES has one-per-application objects :( */
26083SLObjectItf g_maEngineObjectSL = NULL;
26084SLEngineItf g_maEngineSL = NULL;
26085ma_uint32 g_maOpenSLInitCounter = 0;
26086
26087#define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p)))
26088#define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p)))
26089#define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p)))
26090#define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p)))
26091
26092#ifdef MA_ANDROID
26093#define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p)))
26094#else
26095#define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p)))
26096#endif
26097
26098/* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
26099static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id)
26100{
26101 switch (id)
26102 {
26103 case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
26104 case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
26105 case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
26106 case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
26107 case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
26108 case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
26109 case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
26110 case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
26111 case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
26112 case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
26113 case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
26114 case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
26115 case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
26116 case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
26117 case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
26118 case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
26119 case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
26120 case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
26121 default: return 0;
26122 }
26123}
26124
26125/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */
26126static SLuint32 ma_channel_id_to_opensl(ma_uint8 id)
26127{
26128 switch (id)
26129 {
26130 case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER;
26131 case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT;
26132 case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT;
26133 case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER;
26134 case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY;
26135 case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT;
26136 case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT;
26137 case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER;
26138 case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER;
26139 case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER;
26140 case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT;
26141 case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT;
26142 case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER;
26143 case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT;
26144 case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER;
26145 case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT;
26146 case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT;
26147 case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER;
26148 case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT;
26149 default: return 0;
26150 }
26151}
26152
26153/* Converts a channel mapping to an OpenSL-style channel mask. */
26154static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel channelMap[MA_MAX_CHANNELS], ma_uint32 channels)
26155{
26156 SLuint32 channelMask = 0;
26157 ma_uint32 iChannel;
26158 for (iChannel = 0; iChannel < channels; ++iChannel) {
26159 channelMask |= ma_channel_id_to_opensl(channelMap[iChannel]);
26160 }
26161
26162 return channelMask;
26163}
26164
26165/* Converts an OpenSL-style channel mask to a miniaudio channel map. */
26166static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
26167{
26168 if (channels == 1 && channelMask == 0) {
26169 channelMap[0] = MA_CHANNEL_MONO;
26170 } else if (channels == 2 && channelMask == 0) {
26171 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26172 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26173 } else {
26174 if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) {
26175 channelMap[0] = MA_CHANNEL_MONO;
26176 } else {
26177 /* Just iterate over each bit. */
26178 ma_uint32 iChannel = 0;
26179 ma_uint32 iBit;
26180 for (iBit = 0; iBit < 32; ++iBit) {
26181 SLuint32 bitValue = (channelMask & (1UL << iBit));
26182 if (bitValue != 0) {
26183 /* The bit is set. */
26184 channelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue);
26185 iChannel += 1;
26186 }
26187 }
26188 }
26189 }
26190}
26191
26192static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec)
26193{
26194 if (samplesPerSec <= SL_SAMPLINGRATE_8) {
26195 return SL_SAMPLINGRATE_8;
26196 }
26197 if (samplesPerSec <= SL_SAMPLINGRATE_11_025) {
26198 return SL_SAMPLINGRATE_11_025;
26199 }
26200 if (samplesPerSec <= SL_SAMPLINGRATE_12) {
26201 return SL_SAMPLINGRATE_12;
26202 }
26203 if (samplesPerSec <= SL_SAMPLINGRATE_16) {
26204 return SL_SAMPLINGRATE_16;
26205 }
26206 if (samplesPerSec <= SL_SAMPLINGRATE_22_05) {
26207 return SL_SAMPLINGRATE_22_05;
26208 }
26209 if (samplesPerSec <= SL_SAMPLINGRATE_24) {
26210 return SL_SAMPLINGRATE_24;
26211 }
26212 if (samplesPerSec <= SL_SAMPLINGRATE_32) {
26213 return SL_SAMPLINGRATE_32;
26214 }
26215 if (samplesPerSec <= SL_SAMPLINGRATE_44_1) {
26216 return SL_SAMPLINGRATE_44_1;
26217 }
26218 if (samplesPerSec <= SL_SAMPLINGRATE_48) {
26219 return SL_SAMPLINGRATE_48;
26220 }
26221
26222 /* Android doesn't support more than 48000. */
26223#ifndef MA_ANDROID
26224 if (samplesPerSec <= SL_SAMPLINGRATE_64) {
26225 return SL_SAMPLINGRATE_64;
26226 }
26227 if (samplesPerSec <= SL_SAMPLINGRATE_88_2) {
26228 return SL_SAMPLINGRATE_88_2;
26229 }
26230 if (samplesPerSec <= SL_SAMPLINGRATE_96) {
26231 return SL_SAMPLINGRATE_96;
26232 }
26233 if (samplesPerSec <= SL_SAMPLINGRATE_192) {
26234 return SL_SAMPLINGRATE_192;
26235 }
26236#endif
26237
26238 return SL_SAMPLINGRATE_16;
26239}
26240
26241
26242static ma_bool32 ma_context_is_device_id_equal__opensl(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
26243{
26244 MA_ASSERT(pContext != NULL);
26245 MA_ASSERT(pID0 != NULL);
26246 MA_ASSERT(pID1 != NULL);
26247 (void)pContext;
26248
26249 return pID0->opensl == pID1->opensl;
26250}
26251
26252static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
26253{
26254 ma_bool32 cbResult;
26255
26256 MA_ASSERT(pContext != NULL);
26257 MA_ASSERT(callback != NULL);
26258
26259 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. */
26260 if (g_maOpenSLInitCounter == 0) {
26261 return MA_INVALID_OPERATION;
26262 }
26263
26264 /*
26265 TODO: Test Me.
26266
26267 This is currently untested, so for now we are just returning default devices.
26268 */
26269#if 0 && !defined(MA_ANDROID)
26270 ma_bool32 isTerminated = MA_FALSE;
26271
26272 SLuint32 pDeviceIDs[128];
26273 SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]);
26274
26275 SLAudioIODeviceCapabilitiesItf deviceCaps;
26276 SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
26277 if (resultSL != SL_RESULT_SUCCESS) {
26278 /* The interface may not be supported so just report a default device. */
26279 goto return_default_device;
26280 }
26281
26282 /* Playback */
26283 if (!isTerminated) {
26284 resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs);
26285 if (resultSL != SL_RESULT_SUCCESS) {
26286 return MA_NO_DEVICE;
26287 }
26288
26289 for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
26290 ma_device_info deviceInfo;
26291 MA_ZERO_OBJECT(&deviceInfo);
26292 deviceInfo.id.opensl = pDeviceIDs[iDevice];
26293
26294 SLAudioOutputDescriptor desc;
26295 resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
26296 if (resultSL == SL_RESULT_SUCCESS) {
26297 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1);
26298
26299 ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
26300 if (cbResult == MA_FALSE) {
26301 isTerminated = MA_TRUE;
26302 break;
26303 }
26304 }
26305 }
26306 }
26307
26308 /* Capture */
26309 if (!isTerminated) {
26310 resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs);
26311 if (resultSL != SL_RESULT_SUCCESS) {
26312 return MA_NO_DEVICE;
26313 }
26314
26315 for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
26316 ma_device_info deviceInfo;
26317 MA_ZERO_OBJECT(&deviceInfo);
26318 deviceInfo.id.opensl = pDeviceIDs[iDevice];
26319
26320 SLAudioInputDescriptor desc;
26321 resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
26322 if (resultSL == SL_RESULT_SUCCESS) {
26323 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1);
26324
26325 ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
26326 if (cbResult == MA_FALSE) {
26327 isTerminated = MA_TRUE;
26328 break;
26329 }
26330 }
26331 }
26332 }
26333
26334 return MA_SUCCESS;
26335#else
26336 goto return_default_device;
26337#endif
26338
26339return_default_device:;
26340 cbResult = MA_TRUE;
26341
26342 /* Playback. */
26343 if (cbResult) {
26344 ma_device_info deviceInfo;
26345 MA_ZERO_OBJECT(&deviceInfo);
26346 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
26347 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
26348 }
26349
26350 /* Capture. */
26351 if (cbResult) {
26352 ma_device_info deviceInfo;
26353 MA_ZERO_OBJECT(&deviceInfo);
26354 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
26355 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
26356 }
26357
26358 return MA_SUCCESS;
26359}
26360
26361static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
26362{
26363 MA_ASSERT(pContext != NULL);
26364
26365 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. */
26366 if (g_maOpenSLInitCounter == 0) {
26367 return MA_INVALID_OPERATION;
26368 }
26369
26370 /* No exclusive mode with OpenSL|ES. */
26371 if (shareMode == ma_share_mode_exclusive) {
26372 return MA_SHARE_MODE_NOT_SUPPORTED;
26373 }
26374
26375 /*
26376 TODO: Test Me.
26377
26378 This is currently untested, so for now we are just returning default devices.
26379 */
26380#if 0 && !defined(MA_ANDROID)
26381 SLAudioIODeviceCapabilitiesItf deviceCaps;
26382 SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
26383 if (resultSL != SL_RESULT_SUCCESS) {
26384 /* The interface may not be supported so just report a default device. */
26385 goto return_default_device;
26386 }
26387
26388 if (deviceType == ma_device_type_playback) {
26389 SLAudioOutputDescriptor desc;
26390 resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
26391 if (resultSL != SL_RESULT_SUCCESS) {
26392 return MA_NO_DEVICE;
26393 }
26394
26395 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1);
26396 } else {
26397 SLAudioInputDescriptor desc;
26398 resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
26399 if (resultSL != SL_RESULT_SUCCESS) {
26400 return MA_NO_DEVICE;
26401 }
26402
26403 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1);
26404 }
26405
26406 goto return_detailed_info;
26407#else
26408 goto return_default_device;
26409#endif
26410
26411return_default_device:
26412 if (pDeviceID != NULL) {
26413 if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) ||
26414 (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) {
26415 return MA_NO_DEVICE; /* Don't know the device. */
26416 }
26417 }
26418
26419 /* Name / Description */
26420 if (deviceType == ma_device_type_playback) {
26421 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
26422 } else {
26423 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
26424 }
26425
26426 goto return_detailed_info;
26427
26428
26429return_detailed_info:
26430
26431 /*
26432 For now we're just outputting a set of values that are supported by the API but not necessarily supported
26433 by the device natively. Later on we should work on this so that it more closely reflects the device's
26434 actual native format.
26435 */
26436 pDeviceInfo->minChannels = 1;
26437 pDeviceInfo->maxChannels = 2;
26438 pDeviceInfo->minSampleRate = 8000;
26439 pDeviceInfo->maxSampleRate = 48000;
26440 pDeviceInfo->formatCount = 2;
26441 pDeviceInfo->formats[0] = ma_format_u8;
26442 pDeviceInfo->formats[1] = ma_format_s16;
26443#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
26444 pDeviceInfo->formats[pDeviceInfo->formatCount] = ma_format_f32;
26445 pDeviceInfo->formatCount += 1;
26446#endif
26447
26448 return MA_SUCCESS;
26449}
26450
26451
26452#ifdef MA_ANDROID
26453/*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/
26454static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
26455{
26456 ma_device* pDevice = (ma_device*)pUserData;
26457 size_t periodSizeInBytes;
26458 ma_uint8* pBuffer;
26459 SLresult resultSL;
26460
26461 MA_ASSERT(pDevice != NULL);
26462
26463 (void)pBufferQueue;
26464
26465 /*
26466 For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like
26467 OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this,
26468 but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :(
26469 */
26470
26471 /* Don't do anything if the device is not started. */
26472 if (pDevice->state != MA_STATE_STARTED) {
26473 return;
26474 }
26475
26476 /* Don't do anything if the device is being drained. */
26477 if (pDevice->opensl.isDrainingCapture) {
26478 return;
26479 }
26480
26481 periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
26482 pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes);
26483
26484 if (pDevice->type == ma_device_type_duplex) {
26485 ma_device__handle_duplex_callback_capture(pDevice, pDevice->capture.internalPeriodSizeInFrames, pBuffer, &pDevice->opensl.duplexRB);
26486 } else {
26487 ma_device__send_frames_to_client(pDevice, pDevice->capture.internalPeriodSizeInFrames, pBuffer);
26488 }
26489
26490 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes);
26491 if (resultSL != SL_RESULT_SUCCESS) {
26492 return;
26493 }
26494
26495 pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods;
26496}
26497
26498static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
26499{
26500 ma_device* pDevice = (ma_device*)pUserData;
26501 size_t periodSizeInBytes;
26502 ma_uint8* pBuffer;
26503 SLresult resultSL;
26504
26505 MA_ASSERT(pDevice != NULL);
26506
26507 (void)pBufferQueue;
26508
26509 /* Don't do anything if the device is not started. */
26510 if (pDevice->state != MA_STATE_STARTED) {
26511 return;
26512 }
26513
26514 /* Don't do anything if the device is being drained. */
26515 if (pDevice->opensl.isDrainingPlayback) {
26516 return;
26517 }
26518
26519 periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
26520 pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes);
26521
26522 if (pDevice->type == ma_device_type_duplex) {
26523 ma_device__handle_duplex_callback_playback(pDevice, pDevice->playback.internalPeriodSizeInFrames, pBuffer, &pDevice->opensl.duplexRB);
26524 } else {
26525 ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames, pBuffer);
26526 }
26527
26528 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes);
26529 if (resultSL != SL_RESULT_SUCCESS) {
26530 return;
26531 }
26532
26533 pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods;
26534}
26535#endif
26536
26537static void ma_device_uninit__opensl(ma_device* pDevice)
26538{
26539 MA_ASSERT(pDevice != NULL);
26540
26541 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. */
26542 if (g_maOpenSLInitCounter == 0) {
26543 return;
26544 }
26545
26546 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26547 if (pDevice->opensl.pAudioRecorderObj) {
26548 MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj);
26549 }
26550
26551 ma__free_from_callbacks(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks);
26552 }
26553
26554 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26555 if (pDevice->opensl.pAudioPlayerObj) {
26556 MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj);
26557 }
26558 if (pDevice->opensl.pOutputMixObj) {
26559 MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj);
26560 }
26561
26562 ma__free_from_callbacks(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks);
26563 }
26564
26565 if (pDevice->type == ma_device_type_duplex) {
26566 ma_pcm_rb_uninit(&pDevice->opensl.duplexRB);
26567 }
26568}
26569
26570#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
26571typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM;
26572#else
26573typedef SLDataFormat_PCM ma_SLDataFormat_PCM;
26574#endif
26575
26576static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat)
26577{
26578#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
26579 if (format == ma_format_f32) {
26580 pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
26581 pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
26582 } else {
26583 pDataFormat->formatType = SL_DATAFORMAT_PCM;
26584 }
26585#else
26586 pDataFormat->formatType = SL_DATAFORMAT_PCM;
26587#endif
26588
26589 pDataFormat->numChannels = channels;
26590 ((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 */
26591 pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format)*8;
26592 pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels);
26593 pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
26594
26595 /*
26596 Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html
26597 - Only mono and stereo is supported.
26598 - Only u8 and s16 formats are supported.
26599 - Maximum sample rate of 48000.
26600 */
26601#ifdef MA_ANDROID
26602 if (pDataFormat->numChannels > 2) {
26603 pDataFormat->numChannels = 2;
26604 }
26605#if __ANDROID_API__ >= 21
26606 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
26607 /* It's floating point. */
26608 MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
26609 if (pDataFormat->bitsPerSample > 32) {
26610 pDataFormat->bitsPerSample = 32;
26611 }
26612 } else {
26613 if (pDataFormat->bitsPerSample > 16) {
26614 pDataFormat->bitsPerSample = 16;
26615 }
26616 }
26617#else
26618 if (pDataFormat->bitsPerSample > 16) {
26619 pDataFormat->bitsPerSample = 16;
26620 }
26621#endif
26622 if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) {
26623 ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48;
26624 }
26625#endif
26626
26627 pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */
26628
26629 return MA_SUCCESS;
26630}
26631
26632static ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap)
26633{
26634 ma_bool32 isFloatingPoint = MA_FALSE;
26635#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
26636 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
26637 MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
26638 isFloatingPoint = MA_TRUE;
26639 }
26640#endif
26641 if (isFloatingPoint) {
26642 if (pDataFormat->bitsPerSample == 32) {
26643 *pFormat = ma_format_f32;
26644 }
26645 } else {
26646 if (pDataFormat->bitsPerSample == 8) {
26647 *pFormat = ma_format_u8;
26648 } else if (pDataFormat->bitsPerSample == 16) {
26649 *pFormat = ma_format_s16;
26650 } else if (pDataFormat->bitsPerSample == 24) {
26651 *pFormat = ma_format_s24;
26652 } else if (pDataFormat->bitsPerSample == 32) {
26653 *pFormat = ma_format_s32;
26654 }
26655 }
26656
26657 *pChannels = pDataFormat->numChannels;
26658 *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000;
26659 ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, pDataFormat->numChannels, pChannelMap);
26660
26661 return MA_SUCCESS;
26662}
26663
26664static ma_result ma_device_init__opensl(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
26665{
26666#ifdef MA_ANDROID
26667 SLDataLocator_AndroidSimpleBufferQueue queue;
26668 SLresult resultSL;
26669 ma_uint32 periodSizeInFrames;
26670 size_t bufferSizeInBytes;
26671 const SLInterfaceID itfIDs1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
26672 const SLboolean itfIDsRequired1[] = {SL_BOOLEAN_TRUE};
26673#endif
26674
26675 (void)pContext;
26676
26677 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. */
26678 if (g_maOpenSLInitCounter == 0) {
26679 return MA_INVALID_OPERATION;
26680 }
26681
26682 if (pConfig->deviceType == ma_device_type_loopback) {
26683 return MA_DEVICE_TYPE_NOT_SUPPORTED;
26684 }
26685
26686 /*
26687 For now, only supporting Android implementations of OpenSL|ES since that's the only one I've
26688 been able to test with and I currently depend on Android-specific extensions (simple buffer
26689 queues).
26690 */
26691#ifdef MA_ANDROID
26692 /* No exclusive mode with OpenSL|ES. */
26693 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
26694 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
26695 return MA_SHARE_MODE_NOT_SUPPORTED;
26696 }
26697
26698 /* Now we can start initializing the device properly. */
26699 MA_ASSERT(pDevice != NULL);
26700 MA_ZERO_OBJECT(&pDevice->opensl);
26701
26702 queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
26703 queue.numBuffers = pConfig->periods;
26704
26705
26706 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
26707 ma_SLDataFormat_PCM pcm;
26708 SLDataLocator_IODevice locatorDevice;
26709 SLDataSource source;
26710 SLDataSink sink;
26711
26712 ma_SLDataFormat_PCM_init__opensl(pConfig->capture.format, pConfig->capture.channels, pConfig->sampleRate, pConfig->capture.channelMap, &pcm);
26713
26714 locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE;
26715 locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT;
26716 locatorDevice.deviceID = (pConfig->capture.pDeviceID == NULL) ? SL_DEFAULTDEVICEID_AUDIOINPUT : pConfig->capture.pDeviceID->opensl;
26717 locatorDevice.device = NULL;
26718
26719 source.pLocator = &locatorDevice;
26720 source.pFormat = NULL;
26721
26722 sink.pLocator = &queue;
26723 sink.pFormat = (SLDataFormat_PCM*)&pcm;
26724
26725 resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
26726 if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) {
26727 /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
26728 pcm.formatType = SL_DATAFORMAT_PCM;
26729 pcm.numChannels = 1;
26730 ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */
26731 pcm.bitsPerSample = 16;
26732 pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
26733 pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
26734 resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
26735 }
26736
26737 if (resultSL != SL_RESULT_SUCCESS) {
26738 ma_device_uninit__opensl(pDevice);
26739 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
26740 }
26741
26742 if (MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE) != SL_RESULT_SUCCESS) {
26743 ma_device_uninit__opensl(pDevice);
26744 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
26745 }
26746
26747 if (MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_IID_RECORD, &pDevice->opensl.pAudioRecorder) != SL_RESULT_SUCCESS) {
26748 ma_device_uninit__opensl(pDevice);
26749 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
26750 }
26751
26752 if (MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture) != SL_RESULT_SUCCESS) {
26753 ma_device_uninit__opensl(pDevice);
26754 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
26755 }
26756
26757 if (MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice) != SL_RESULT_SUCCESS) {
26758 ma_device_uninit__opensl(pDevice);
26759 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
26760 }
26761
26762 /* The internal format is determined by the "pcm" object. */
26763 ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDevice->capture.internalFormat, &pDevice->capture.internalChannels, &pDevice->capture.internalSampleRate, pDevice->capture.internalChannelMap);
26764
26765 /* Buffer. */
26766 periodSizeInFrames = pConfig->periodSizeInFrames;
26767 if (periodSizeInFrames == 0) {
26768 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->capture.internalSampleRate);
26769 }
26770 pDevice->capture.internalPeriods = pConfig->periods;
26771 pDevice->capture.internalPeriodSizeInFrames = periodSizeInFrames;
26772 pDevice->opensl.currentBufferIndexCapture = 0;
26773
26774 bufferSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels) * pDevice->capture.internalPeriods;
26775 pDevice->opensl.pBufferCapture = (ma_uint8*)ma__calloc_from_callbacks(bufferSizeInBytes, &pContext->allocationCallbacks);
26776 if (pDevice->opensl.pBufferCapture == NULL) {
26777 ma_device_uninit__opensl(pDevice);
26778 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.", MA_OUT_OF_MEMORY);
26779 }
26780 MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes);
26781 }
26782
26783 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
26784 ma_SLDataFormat_PCM pcm;
26785 SLDataSource source;
26786 SLDataLocator_OutputMix outmixLocator;
26787 SLDataSink sink;
26788
26789 ma_SLDataFormat_PCM_init__opensl(pConfig->playback.format, pConfig->playback.channels, pConfig->sampleRate, pConfig->playback.channelMap, &pcm);
26790
26791 resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL);
26792 if (resultSL != SL_RESULT_SUCCESS) {
26793 ma_device_uninit__opensl(pDevice);
26794 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
26795 }
26796
26797 if (MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE)) {
26798 ma_device_uninit__opensl(pDevice);
26799 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
26800 }
26801
26802 if (MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix) != SL_RESULT_SUCCESS) {
26803 ma_device_uninit__opensl(pDevice);
26804 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
26805 }
26806
26807 /* Set the output device. */
26808 if (pConfig->playback.pDeviceID != NULL) {
26809 SLuint32 deviceID_OpenSL = pConfig->playback.pDeviceID->opensl;
26810 MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL);
26811 }
26812
26813 source.pLocator = &queue;
26814 source.pFormat = (SLDataFormat_PCM*)&pcm;
26815
26816 outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;
26817 outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj;
26818
26819 sink.pLocator = &outmixLocator;
26820 sink.pFormat = NULL;
26821
26822 resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
26823 if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) {
26824 /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
26825 pcm.formatType = SL_DATAFORMAT_PCM;
26826 pcm.numChannels = 2;
26827 ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16;
26828 pcm.bitsPerSample = 16;
26829 pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
26830 pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
26831 resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
26832 }
26833
26834 if (resultSL != SL_RESULT_SUCCESS) {
26835 ma_device_uninit__opensl(pDevice);
26836 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
26837 }
26838
26839 if (MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE) != SL_RESULT_SUCCESS) {
26840 ma_device_uninit__opensl(pDevice);
26841 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
26842 }
26843
26844 if (MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_IID_PLAY, &pDevice->opensl.pAudioPlayer) != SL_RESULT_SUCCESS) {
26845 ma_device_uninit__opensl(pDevice);
26846 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
26847 }
26848
26849 if (MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback) != SL_RESULT_SUCCESS) {
26850 ma_device_uninit__opensl(pDevice);
26851 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
26852 }
26853
26854 if (MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice) != SL_RESULT_SUCCESS) {
26855 ma_device_uninit__opensl(pDevice);
26856 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
26857 }
26858
26859 /* The internal format is determined by the "pcm" object. */
26860 ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDevice->playback.internalFormat, &pDevice->playback.internalChannels, &pDevice->playback.internalSampleRate, pDevice->playback.internalChannelMap);
26861
26862 /* Buffer. */
26863 periodSizeInFrames = pConfig->periodSizeInFrames;
26864 if (periodSizeInFrames == 0) {
26865 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->playback.internalSampleRate);
26866 }
26867 pDevice->playback.internalPeriods = pConfig->periods;
26868 pDevice->playback.internalPeriodSizeInFrames = periodSizeInFrames;
26869 pDevice->opensl.currentBufferIndexPlayback = 0;
26870
26871 bufferSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels) * pDevice->playback.internalPeriods;
26872 pDevice->opensl.pBufferPlayback = (ma_uint8*)ma__calloc_from_callbacks(bufferSizeInBytes, &pContext->allocationCallbacks);
26873 if (pDevice->opensl.pBufferPlayback == NULL) {
26874 ma_device_uninit__opensl(pDevice);
26875 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.", MA_OUT_OF_MEMORY);
26876 }
26877 MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes);
26878 }
26879
26880 if (pConfig->deviceType == ma_device_type_duplex) {
26881 ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames) * pDevice->capture.internalPeriods;
26882 ma_result result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->pContext->allocationCallbacks, &pDevice->opensl.duplexRB);
26883 if (result != MA_SUCCESS) {
26884 ma_device_uninit__opensl(pDevice);
26885 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to initialize ring buffer.", result);
26886 }
26887
26888 /* We need a period to act as a buffer for cases where the playback and capture device's end up desyncing. */
26889 {
26890 ma_uint32 marginSizeInFrames = rbSizeInFrames / pDevice->capture.internalPeriods;
26891 void* pMarginData;
26892 ma_pcm_rb_acquire_write(&pDevice->opensl.duplexRB, &marginSizeInFrames, &pMarginData);
26893 {
26894 MA_ZERO_MEMORY(pMarginData, marginSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
26895 }
26896 ma_pcm_rb_commit_write(&pDevice->opensl.duplexRB, marginSizeInFrames, pMarginData);
26897 }
26898 }
26899
26900 return MA_SUCCESS;
26901#else
26902 return MA_NO_BACKEND; /* Non-Android implementations are not supported. */
26903#endif
26904}
26905
26906static ma_result ma_device_start__opensl(ma_device* pDevice)
26907{
26908 SLresult resultSL;
26909 size_t periodSizeInBytes;
26910 ma_uint32 iPeriod;
26911
26912 MA_ASSERT(pDevice != NULL);
26913
26914 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. */
26915 if (g_maOpenSLInitCounter == 0) {
26916 return MA_INVALID_OPERATION;
26917 }
26918
26919 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26920 resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING);
26921 if (resultSL != SL_RESULT_SUCCESS) {
26922 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device.", MA_FAILED_TO_START_BACKEND_DEVICE);
26923 }
26924
26925 periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
26926 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
26927 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes);
26928 if (resultSL != SL_RESULT_SUCCESS) {
26929 MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
26930 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device.", MA_FAILED_TO_START_BACKEND_DEVICE);
26931 }
26932 }
26933 }
26934
26935 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26936 resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING);
26937 if (resultSL != SL_RESULT_SUCCESS) {
26938 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device.", MA_FAILED_TO_START_BACKEND_DEVICE);
26939 }
26940
26941 /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueu silent buffers. */
26942 if (pDevice->type == ma_device_type_duplex) {
26943 MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
26944 } else {
26945 ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback);
26946 }
26947
26948 periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
26949 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
26950 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes);
26951 if (resultSL != SL_RESULT_SUCCESS) {
26952 MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
26953 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device.", MA_FAILED_TO_START_BACKEND_DEVICE);
26954 }
26955 }
26956 }
26957
26958 return MA_SUCCESS;
26959}
26960
26961static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType)
26962{
26963 SLAndroidSimpleBufferQueueItf pBufferQueue;
26964
26965 MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback);
26966
26967 if (pDevice->type == ma_device_type_capture) {
26968 pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture;
26969 pDevice->opensl.isDrainingCapture = MA_TRUE;
26970 } else {
26971 pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback;
26972 pDevice->opensl.isDrainingPlayback = MA_TRUE;
26973 }
26974
26975 for (;;) {
26976 SLAndroidSimpleBufferQueueState state;
26977
26978 MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state);
26979 if (state.count == 0) {
26980 break;
26981 }
26982
26983 ma_sleep(10);
26984 }
26985
26986 if (pDevice->type == ma_device_type_capture) {
26987 pDevice->opensl.isDrainingCapture = MA_FALSE;
26988 } else {
26989 pDevice->opensl.isDrainingPlayback = MA_FALSE;
26990 }
26991
26992 return MA_SUCCESS;
26993}
26994
26995static ma_result ma_device_stop__opensl(ma_device* pDevice)
26996{
26997 SLresult resultSL;
26998 ma_stop_proc onStop;
26999
27000 MA_ASSERT(pDevice != NULL);
27001
27002 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. */
27003 if (g_maOpenSLInitCounter == 0) {
27004 return MA_INVALID_OPERATION;
27005 }
27006
27007 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27008 ma_device_drain__opensl(pDevice, ma_device_type_capture);
27009
27010 resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
27011 if (resultSL != SL_RESULT_SUCCESS) {
27012 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
27013 }
27014
27015 MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture);
27016 }
27017
27018 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
27019 ma_device_drain__opensl(pDevice, ma_device_type_playback);
27020
27021 resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
27022 if (resultSL != SL_RESULT_SUCCESS) {
27023 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
27024 }
27025
27026 MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback);
27027 }
27028
27029 /* 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. */
27030 onStop = pDevice->onStop;
27031 if (onStop) {
27032 onStop(pDevice);
27033 }
27034
27035 return MA_SUCCESS;
27036}
27037
27038
27039static ma_result ma_context_uninit__opensl(ma_context* pContext)
27040{
27041 MA_ASSERT(pContext != NULL);
27042 MA_ASSERT(pContext->backend == ma_backend_opensl);
27043 (void)pContext;
27044
27045 /* Uninit global data. */
27046 if (g_maOpenSLInitCounter > 0) {
27047 if (ma_atomic_decrement_32(&g_maOpenSLInitCounter) == 0) {
27048 (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
27049 }
27050 }
27051
27052 return MA_SUCCESS;
27053}
27054
27055static ma_result ma_context_init__opensl(const ma_context_config* pConfig, ma_context* pContext)
27056{
27057 MA_ASSERT(pContext != NULL);
27058
27059 (void)pConfig;
27060
27061 /* Initialize global data first if applicable. */
27062 if (ma_atomic_increment_32(&g_maOpenSLInitCounter) == 1) {
27063 SLresult resultSL = slCreateEngine(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL);
27064 if (resultSL != SL_RESULT_SUCCESS) {
27065 ma_atomic_decrement_32(&g_maOpenSLInitCounter);
27066 return MA_NO_BACKEND;
27067 }
27068
27069 (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE);
27070
27071 resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_ENGINE, &g_maEngineSL);
27072 if (resultSL != SL_RESULT_SUCCESS) {
27073 (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
27074 ma_atomic_decrement_32(&g_maOpenSLInitCounter);
27075 return MA_NO_BACKEND;
27076 }
27077 }
27078
27079 pContext->isBackendAsynchronous = MA_TRUE;
27080
27081 pContext->onUninit = ma_context_uninit__opensl;
27082 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__opensl;
27083 pContext->onEnumDevices = ma_context_enumerate_devices__opensl;
27084 pContext->onGetDeviceInfo = ma_context_get_device_info__opensl;
27085 pContext->onDeviceInit = ma_device_init__opensl;
27086 pContext->onDeviceUninit = ma_device_uninit__opensl;
27087 pContext->onDeviceStart = ma_device_start__opensl;
27088 pContext->onDeviceStop = ma_device_stop__opensl;
27089
27090 return MA_SUCCESS;
27091}
27092#endif /* OpenSL|ES */
27093
27094
27095/******************************************************************************
27096
27097Web Audio Backend
27098
27099******************************************************************************/
27100#ifdef MA_HAS_WEBAUDIO
27101#include <emscripten/emscripten.h>
27102
27103static ma_bool32 ma_is_capture_supported__webaudio()
27104{
27105 return EM_ASM_INT({
27106 return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined);
27107 }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */
27108}
27109
27110#ifdef __cplusplus
27111extern "C" {
27112#endif
27113EMSCRIPTEN_KEEPALIVE void ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
27114{
27115 if (pDevice->type == ma_device_type_duplex) {
27116 ma_device__handle_duplex_callback_capture(pDevice, (ma_uint32)frameCount, pFrames, &pDevice->webaudio.duplexRB);
27117 } else {
27118 ma_device__send_frames_to_client(pDevice, (ma_uint32)frameCount, pFrames); /* Send directly to the client. */
27119 }
27120}
27121
27122EMSCRIPTEN_KEEPALIVE void ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
27123{
27124 if (pDevice->type == ma_device_type_duplex) {
27125 ma_device__handle_duplex_callback_playback(pDevice, (ma_uint32)frameCount, pFrames, &pDevice->webaudio.duplexRB);
27126 } else {
27127 ma_device__read_frames_from_client(pDevice, (ma_uint32)frameCount, pFrames); /* Read directly from the device. */
27128 }
27129}
27130#ifdef __cplusplus
27131}
27132#endif
27133
27134static ma_bool32 ma_context_is_device_id_equal__webaudio(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
27135{
27136 MA_ASSERT(pContext != NULL);
27137 MA_ASSERT(pID0 != NULL);
27138 MA_ASSERT(pID1 != NULL);
27139 (void)pContext;
27140
27141 return ma_strcmp(pID0->webaudio, pID1->webaudio) == 0;
27142}
27143
27144static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
27145{
27146 ma_bool32 cbResult = MA_TRUE;
27147
27148 MA_ASSERT(pContext != NULL);
27149 MA_ASSERT(callback != NULL);
27150
27151 /* Only supporting default devices for now. */
27152
27153 /* Playback. */
27154 if (cbResult) {
27155 ma_device_info deviceInfo;
27156 MA_ZERO_OBJECT(&deviceInfo);
27157 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
27158 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
27159 }
27160
27161 /* Capture. */
27162 if (cbResult) {
27163 if (ma_is_capture_supported__webaudio()) {
27164 ma_device_info deviceInfo;
27165 MA_ZERO_OBJECT(&deviceInfo);
27166 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
27167 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
27168 }
27169 }
27170
27171 return MA_SUCCESS;
27172}
27173
27174static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
27175{
27176 MA_ASSERT(pContext != NULL);
27177
27178 /* No exclusive mode with Web Audio. */
27179 if (shareMode == ma_share_mode_exclusive) {
27180 return MA_SHARE_MODE_NOT_SUPPORTED;
27181 }
27182
27183 if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
27184 return MA_NO_DEVICE;
27185 }
27186
27187
27188 MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio));
27189
27190 /* Only supporting default devices for now. */
27191 if (deviceType == ma_device_type_playback) {
27192 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
27193 } else {
27194 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
27195 }
27196
27197 /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */
27198 pDeviceInfo->minChannels = 1;
27199 pDeviceInfo->maxChannels = MA_MAX_CHANNELS;
27200 if (pDeviceInfo->maxChannels > 32) {
27201 pDeviceInfo->maxChannels = 32; /* Maximum output channel count is 32 for createScriptProcessor() (JavaScript). */
27202 }
27203
27204 /* We can query the sample rate by just using a temporary audio context. */
27205 pDeviceInfo->minSampleRate = EM_ASM_INT({
27206 try {
27207 var temp = new (window.AudioContext || window.webkitAudioContext)();
27208 var sampleRate = temp.sampleRate;
27209 temp.close();
27210 return sampleRate;
27211 } catch(e) {
27212 return 0;
27213 }
27214 }, 0); /* Must pass in a dummy argument for C99 compatibility. */
27215 pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
27216 if (pDeviceInfo->minSampleRate == 0) {
27217 return MA_NO_DEVICE;
27218 }
27219
27220 /* Web Audio only supports f32. */
27221 pDeviceInfo->formatCount = 1;
27222 pDeviceInfo->formats[0] = ma_format_f32;
27223
27224 return MA_SUCCESS;
27225}
27226
27227
27228static void ma_device_uninit_by_index__webaudio(ma_device* pDevice, ma_device_type deviceType, int deviceIndex)
27229{
27230 MA_ASSERT(pDevice != NULL);
27231
27232 EM_ASM({
27233 var device = miniaudio.get_device_by_index($0);
27234
27235 /* Make sure all nodes are disconnected and marked for collection. */
27236 if (device.scriptNode !== undefined) {
27237 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... */
27238 device.scriptNode.disconnect();
27239 device.scriptNode = undefined;
27240 }
27241 if (device.streamNode !== undefined) {
27242 device.streamNode.disconnect();
27243 device.streamNode = undefined;
27244 }
27245
27246 /*
27247 Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want
27248 to clear the callback before closing.
27249 */
27250 device.webaudio.close();
27251 device.webaudio = undefined;
27252
27253 /* Can't forget to free the intermediary buffer. This is the buffer that's shared between JavaScript and C. */
27254 if (device.intermediaryBuffer !== undefined) {
27255 Module._free(device.intermediaryBuffer);
27256 device.intermediaryBuffer = undefined;
27257 device.intermediaryBufferView = undefined;
27258 device.intermediaryBufferSizeInBytes = undefined;
27259 }
27260
27261 /* Make sure the device is untracked so the slot can be reused later. */
27262 miniaudio.untrack_device_by_index($0);
27263 }, deviceIndex, deviceType);
27264}
27265
27266static void ma_device_uninit__webaudio(ma_device* pDevice)
27267{
27268 MA_ASSERT(pDevice != NULL);
27269
27270 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27271 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
27272 }
27273
27274 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
27275 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback);
27276 }
27277
27278 if (pDevice->type == ma_device_type_duplex) {
27279 ma_pcm_rb_uninit(&pDevice->webaudio.duplexRB);
27280 }
27281}
27282
27283static ma_result ma_device_init_by_type__webaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
27284{
27285 int deviceIndex;
27286 ma_uint32 internalPeriodSizeInFrames;
27287
27288 MA_ASSERT(pContext != NULL);
27289 MA_ASSERT(pConfig != NULL);
27290 MA_ASSERT(deviceType != ma_device_type_duplex);
27291 MA_ASSERT(pDevice != NULL);
27292
27293 if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
27294 return MA_NO_DEVICE;
27295 }
27296
27297 /* Try calculating an appropriate buffer size. */
27298 internalPeriodSizeInFrames = pConfig->periodSizeInFrames;
27299 if (internalPeriodSizeInFrames == 0) {
27300 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pConfig->sampleRate);
27301 }
27302
27303 /* The size of the buffer must be a power of 2 and between 256 and 16384. */
27304 if (internalPeriodSizeInFrames < 256) {
27305 internalPeriodSizeInFrames = 256;
27306 } else if (internalPeriodSizeInFrames > 16384) {
27307 internalPeriodSizeInFrames = 16384;
27308 } else {
27309 internalPeriodSizeInFrames = ma_next_power_of_2(internalPeriodSizeInFrames);
27310 }
27311
27312 /* 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. */
27313 deviceIndex = EM_ASM_INT({
27314 var channels = $0;
27315 var sampleRate = $1;
27316 var bufferSize = $2; /* In PCM frames. */
27317 var isCapture = $3;
27318 var pDevice = $4;
27319
27320 if (typeof(miniaudio) === 'undefined') {
27321 return -1; /* Context not initialized. */
27322 }
27323
27324 var device = {};
27325
27326 /* The AudioContext must be created in a suspended state. */
27327 device.webaudio = new (window.AudioContext || window.webkitAudioContext)({sampleRate:sampleRate});
27328 device.webaudio.suspend();
27329
27330 /*
27331 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
27332 JavaScript and C it needs to be allocated and freed using Module._malloc() and Module._free().
27333 */
27334 device.intermediaryBufferSizeInBytes = channels * bufferSize * 4;
27335 device.intermediaryBuffer = Module._malloc(device.intermediaryBufferSizeInBytes);
27336 device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
27337
27338 /*
27339 Both playback and capture devices use a ScriptProcessorNode for performing per-sample operations.
27340
27341 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
27342 that's periodically fired, just like a normal audio callback function. But apparently this design is "flawed" and is now deprecated in favour of
27343 something called AudioWorklets which _forces_ you to load a _separate_ .js file at run time... nice... Hopefully ScriptProcessorNode will continue to
27344 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
27345 implementation. I'll be avoiding that insane AudioWorklet API like the plague...
27346
27347 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
27348 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
27349 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
27350 been unable to figure out how to get this as raw PCM. The closes I can think is to use the MIME type for WAV files and just parse it, but I don't know
27351 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
27352 this for now. If anything knows how I could get raw PCM data using the MediaRecorder API please let me know!
27353 */
27354 device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channels, channels);
27355
27356 if (isCapture) {
27357 device.scriptNode.onaudioprocess = function(e) {
27358 if (device.intermediaryBuffer === undefined) {
27359 return; /* This means the device has been uninitialized. */
27360 }
27361
27362 /* Make sure silence it output to the AudioContext destination. Not doing this will cause sound to come out of the speakers! */
27363 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
27364 e.outputBuffer.getChannelData(iChannel).fill(0.0);
27365 }
27366
27367 /* There are some situations where we may want to send silence to the client. */
27368 var sendSilence = false;
27369 if (device.streamNode === undefined) {
27370 sendSilence = true;
27371 }
27372
27373 /* Sanity check. This will never happen, right? */
27374 if (e.inputBuffer.numberOfChannels != channels) {
27375 console.log("Capture: Channel count mismatch. " + e.inputBufer.numberOfChannels + " != " + channels + ". Sending silence.");
27376 sendSilence = true;
27377 }
27378
27379 /* This looped design guards against the situation where e.inputBuffer is a different size to the original buffer size. Should never happen in practice. */
27380 var totalFramesProcessed = 0;
27381 while (totalFramesProcessed < e.inputBuffer.length) {
27382 var framesRemaining = e.inputBuffer.length - totalFramesProcessed;
27383 var framesToProcess = framesRemaining;
27384 if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
27385 framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
27386 }
27387
27388 /* 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. */
27389 if (sendSilence) {
27390 device.intermediaryBufferView.fill(0.0);
27391 } else {
27392 for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
27393 for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) {
27394 device.intermediaryBufferView[iFrame*channels + iChannel] = e.inputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame];
27395 }
27396 }
27397 }
27398
27399 /* Send data to the client from our intermediary buffer. */
27400 ccall("ma_device_process_pcm_frames_capture__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]);
27401
27402 totalFramesProcessed += framesToProcess;
27403 }
27404 };
27405
27406 navigator.mediaDevices.getUserMedia({audio:true, video:false})
27407 .then(function(stream) {
27408 device.streamNode = device.webaudio.createMediaStreamSource(stream);
27409 device.streamNode.connect(device.scriptNode);
27410 device.scriptNode.connect(device.webaudio.destination);
27411 })
27412 .catch(function(error) {
27413 /* I think this should output silence... */
27414 device.scriptNode.connect(device.webaudio.destination);
27415 });
27416 } else {
27417 device.scriptNode.onaudioprocess = function(e) {
27418 if (device.intermediaryBuffer === undefined) {
27419 return; /* This means the device has been uninitialized. */
27420 }
27421
27422 var outputSilence = false;
27423
27424 /* Sanity check. This will never happen, right? */
27425 if (e.outputBuffer.numberOfChannels != channels) {
27426 console.log("Playback: Channel count mismatch. " + e.outputBufer.numberOfChannels + " != " + channels + ". Outputting silence.");
27427 outputSilence = true;
27428 return;
27429 }
27430
27431 /* This looped design guards against the situation where e.outputBuffer is a different size to the original buffer size. Should never happen in practice. */
27432 var totalFramesProcessed = 0;
27433 while (totalFramesProcessed < e.outputBuffer.length) {
27434 var framesRemaining = e.outputBuffer.length - totalFramesProcessed;
27435 var framesToProcess = framesRemaining;
27436 if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
27437 framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
27438 }
27439
27440 /* Read data from the client into our intermediary buffer. */
27441 ccall("ma_device_process_pcm_frames_playback__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]);
27442
27443 /* At this point we'll have data in our intermediary buffer which we now need to deinterleave and copy over to the output buffers. */
27444 if (outputSilence) {
27445 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
27446 e.outputBuffer.getChannelData(iChannel).fill(0.0);
27447 }
27448 } else {
27449 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
27450 for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
27451 e.outputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame] = device.intermediaryBufferView[iFrame*channels + iChannel];
27452 }
27453 }
27454 }
27455
27456 totalFramesProcessed += framesToProcess;
27457 }
27458 };
27459
27460 device.scriptNode.connect(device.webaudio.destination);
27461 }
27462
27463 return miniaudio.track_device(device);
27464 }, (deviceType == ma_device_type_capture) ? pConfig->capture.channels : pConfig->playback.channels, pConfig->sampleRate, internalPeriodSizeInFrames, deviceType == ma_device_type_capture, pDevice);
27465
27466 if (deviceIndex < 0) {
27467 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
27468 }
27469
27470 if (deviceType == ma_device_type_capture) {
27471 pDevice->webaudio.indexCapture = deviceIndex;
27472 pDevice->capture.internalFormat = ma_format_f32;
27473 pDevice->capture.internalChannels = pConfig->capture.channels;
27474 ma_get_standard_channel_map(ma_standard_channel_map_webaudio, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
27475 pDevice->capture.internalSampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
27476 pDevice->capture.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
27477 pDevice->capture.internalPeriods = 1;
27478 } else {
27479 pDevice->webaudio.indexPlayback = deviceIndex;
27480 pDevice->playback.internalFormat = ma_format_f32;
27481 pDevice->playback.internalChannels = pConfig->playback.channels;
27482 ma_get_standard_channel_map(ma_standard_channel_map_webaudio, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
27483 pDevice->playback.internalSampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
27484 pDevice->playback.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
27485 pDevice->playback.internalPeriods = 1;
27486 }
27487
27488 return MA_SUCCESS;
27489}
27490
27491static ma_result ma_device_init__webaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
27492{
27493 ma_result result;
27494
27495 if (pConfig->deviceType == ma_device_type_loopback) {
27496 return MA_DEVICE_TYPE_NOT_SUPPORTED;
27497 }
27498
27499 /* No exclusive mode with Web Audio. */
27500 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
27501 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
27502 return MA_SHARE_MODE_NOT_SUPPORTED;
27503 }
27504
27505 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
27506 result = ma_device_init_by_type__webaudio(pContext, pConfig, ma_device_type_capture, pDevice);
27507 if (result != MA_SUCCESS) {
27508 return result;
27509 }
27510 }
27511
27512 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
27513 result = ma_device_init_by_type__webaudio(pContext, pConfig, ma_device_type_playback, pDevice);
27514 if (result != MA_SUCCESS) {
27515 if (pConfig->deviceType == ma_device_type_duplex) {
27516 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
27517 }
27518 return result;
27519 }
27520 }
27521
27522 /*
27523 We need a ring buffer for moving data from the capture device to the playback device. The capture callback is the producer
27524 and the playback callback is the consumer. The buffer needs to be large enough to hold internalPeriodSizeInFrames based on
27525 the external sample rate.
27526 */
27527 if (pConfig->deviceType == ma_device_type_duplex) {
27528 ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames) * 2;
27529 result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->pContext->allocationCallbacks, &pDevice->webaudio.duplexRB);
27530 if (result != MA_SUCCESS) {
27531 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27532 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
27533 }
27534 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
27535 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback);
27536 }
27537 return result;
27538 }
27539
27540 /* We need a period to act as a buffer for cases where the playback and capture device's end up desyncing. */
27541 {
27542 ma_uint32 marginSizeInFrames = rbSizeInFrames / 3; /* <-- Dividing by 3 because internalPeriods is always set to 1 for WebAudio. */
27543 void* pMarginData;
27544 ma_pcm_rb_acquire_write(&pDevice->webaudio.duplexRB, &marginSizeInFrames, &pMarginData);
27545 {
27546 MA_ZERO_MEMORY(pMarginData, marginSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
27547 }
27548 ma_pcm_rb_commit_write(&pDevice->webaudio.duplexRB, marginSizeInFrames, pMarginData);
27549 }
27550 }
27551
27552 return MA_SUCCESS;
27553}
27554
27555static ma_result ma_device_start__webaudio(ma_device* pDevice)
27556{
27557 MA_ASSERT(pDevice != NULL);
27558
27559 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27560 EM_ASM({
27561 miniaudio.get_device_by_index($0).webaudio.resume();
27562 }, pDevice->webaudio.indexCapture);
27563 }
27564
27565 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
27566 EM_ASM({
27567 miniaudio.get_device_by_index($0).webaudio.resume();
27568 }, pDevice->webaudio.indexPlayback);
27569 }
27570
27571 return MA_SUCCESS;
27572}
27573
27574static ma_result ma_device_stop__webaudio(ma_device* pDevice)
27575{
27576 MA_ASSERT(pDevice != NULL);
27577
27578 /*
27579 From the WebAudio API documentation for AudioContext.suspend():
27580
27581 Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the
27582 destination, and then allows the system to release its claim on audio hardware.
27583
27584 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
27585 do any kind of explicit draining.
27586 */
27587
27588 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27589 EM_ASM({
27590 miniaudio.get_device_by_index($0).webaudio.suspend();
27591 }, pDevice->webaudio.indexCapture);
27592 }
27593
27594 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
27595 EM_ASM({
27596 miniaudio.get_device_by_index($0).webaudio.suspend();
27597 }, pDevice->webaudio.indexPlayback);
27598 }
27599
27600 ma_stop_proc onStop = pDevice->onStop;
27601 if (onStop) {
27602 onStop(pDevice);
27603 }
27604
27605 return MA_SUCCESS;
27606}
27607
27608static ma_result ma_context_uninit__webaudio(ma_context* pContext)
27609{
27610 MA_ASSERT(pContext != NULL);
27611 MA_ASSERT(pContext->backend == ma_backend_webaudio);
27612
27613 /* Nothing needs to be done here. */
27614 (void)pContext;
27615
27616 return MA_SUCCESS;
27617}
27618
27619static ma_result ma_context_init__webaudio(const ma_context_config* pConfig, ma_context* pContext)
27620{
27621 int resultFromJS;
27622
27623 MA_ASSERT(pContext != NULL);
27624
27625 /* Here is where our global JavaScript object is initialized. */
27626 resultFromJS = EM_ASM_INT({
27627 if ((window.AudioContext || window.webkitAudioContext) === undefined) {
27628 return 0; /* Web Audio not supported. */
27629 }
27630
27631 if (typeof(miniaudio) === 'undefined') {
27632 miniaudio = {};
27633 miniaudio.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */
27634
27635 miniaudio.track_device = function(device) {
27636 /* Try inserting into a free slot first. */
27637 for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
27638 if (miniaudio.devices[iDevice] == null) {
27639 miniaudio.devices[iDevice] = device;
27640 return iDevice;
27641 }
27642 }
27643
27644 /* Getting here means there is no empty slots in the array so we just push to the end. */
27645 miniaudio.devices.push(device);
27646 return miniaudio.devices.length - 1;
27647 };
27648
27649 miniaudio.untrack_device_by_index = function(deviceIndex) {
27650 /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */
27651 miniaudio.devices[deviceIndex] = null;
27652
27653 /* Trim the array if possible. */
27654 while (miniaudio.devices.length > 0) {
27655 if (miniaudio.devices[miniaudio.devices.length-1] == null) {
27656 miniaudio.devices.pop();
27657 } else {
27658 break;
27659 }
27660 }
27661 };
27662
27663 miniaudio.untrack_device = function(device) {
27664 for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
27665 if (miniaudio.devices[iDevice] == device) {
27666 return miniaudio.untrack_device_by_index(iDevice);
27667 }
27668 }
27669 };
27670
27671 miniaudio.get_device_by_index = function(deviceIndex) {
27672 return miniaudio.devices[deviceIndex];
27673 };
27674 }
27675
27676 return 1;
27677 }, 0); /* Must pass in a dummy argument for C99 compatibility. */
27678
27679 if (resultFromJS != 1) {
27680 return MA_FAILED_TO_INIT_BACKEND;
27681 }
27682
27683
27684 pContext->isBackendAsynchronous = MA_TRUE;
27685
27686 pContext->onUninit = ma_context_uninit__webaudio;
27687 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__webaudio;
27688 pContext->onEnumDevices = ma_context_enumerate_devices__webaudio;
27689 pContext->onGetDeviceInfo = ma_context_get_device_info__webaudio;
27690 pContext->onDeviceInit = ma_device_init__webaudio;
27691 pContext->onDeviceUninit = ma_device_uninit__webaudio;
27692 pContext->onDeviceStart = ma_device_start__webaudio;
27693 pContext->onDeviceStop = ma_device_stop__webaudio;
27694
27695 (void)pConfig; /* Unused. */
27696 return MA_SUCCESS;
27697}
27698#endif /* Web Audio */
27699
27700
27701
27702static ma_bool32 ma__is_channel_map_valid(const ma_channel* channelMap, ma_uint32 channels)
27703{
27704 /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */
27705 if (channelMap[0] != MA_CHANNEL_NONE) {
27706 ma_uint32 iChannel;
27707
27708 if (channels == 0) {
27709 return MA_FALSE; /* No channels. */
27710 }
27711
27712 /* A channel cannot be present in the channel map more than once. */
27713 for (iChannel = 0; iChannel < channels; ++iChannel) {
27714 ma_uint32 jChannel;
27715 for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) {
27716 if (channelMap[iChannel] == channelMap[jChannel]) {
27717 return MA_FALSE;
27718 }
27719 }
27720 }
27721 }
27722
27723 return MA_TRUE;
27724}
27725
27726
27727static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType)
27728{
27729 ma_result result;
27730
27731 MA_ASSERT(pDevice != NULL);
27732
27733 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
27734 if (pDevice->capture.usingDefaultFormat) {
27735 pDevice->capture.format = pDevice->capture.internalFormat;
27736 }
27737 if (pDevice->capture.usingDefaultChannels) {
27738 pDevice->capture.channels = pDevice->capture.internalChannels;
27739 }
27740 if (pDevice->capture.usingDefaultChannelMap) {
27741 if (pDevice->capture.internalChannels == pDevice->capture.channels) {
27742 ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels);
27743 } else {
27744 ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->capture.channels, pDevice->capture.channelMap);
27745 }
27746 }
27747 }
27748
27749 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
27750 if (pDevice->playback.usingDefaultFormat) {
27751 pDevice->playback.format = pDevice->playback.internalFormat;
27752 }
27753 if (pDevice->playback.usingDefaultChannels) {
27754 pDevice->playback.channels = pDevice->playback.internalChannels;
27755 }
27756 if (pDevice->playback.usingDefaultChannelMap) {
27757 if (pDevice->playback.internalChannels == pDevice->playback.channels) {
27758 ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels);
27759 } else {
27760 ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->playback.channels, pDevice->playback.channelMap);
27761 }
27762 }
27763 }
27764
27765 if (pDevice->usingDefaultSampleRate) {
27766 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
27767 pDevice->sampleRate = pDevice->capture.internalSampleRate;
27768 } else {
27769 pDevice->sampleRate = pDevice->playback.internalSampleRate;
27770 }
27771 }
27772
27773 /* PCM converters. */
27774 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
27775 /* Converting from internal device format to client format. */
27776 ma_data_converter_config converterConfig = ma_data_converter_config_init_default();
27777 converterConfig.formatIn = pDevice->capture.internalFormat;
27778 converterConfig.channelsIn = pDevice->capture.internalChannels;
27779 converterConfig.sampleRateIn = pDevice->capture.internalSampleRate;
27780 ma_channel_map_copy(converterConfig.channelMapIn, pDevice->capture.internalChannelMap, pDevice->capture.internalChannels);
27781 converterConfig.formatOut = pDevice->capture.format;
27782 converterConfig.channelsOut = pDevice->capture.channels;
27783 converterConfig.sampleRateOut = pDevice->sampleRate;
27784 ma_channel_map_copy(converterConfig.channelMapOut, pDevice->capture.channelMap, pDevice->capture.channels);
27785 converterConfig.resampling.allowDynamicSampleRate = MA_FALSE;
27786 converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
27787 converterConfig.resampling.linear.lpfCount = pDevice->resampling.linear.lpfCount;
27788 converterConfig.resampling.speex.quality = pDevice->resampling.speex.quality;
27789
27790 result = ma_data_converter_init(&converterConfig, &pDevice->capture.converter);
27791 if (result != MA_SUCCESS) {
27792 return result;
27793 }
27794 }
27795
27796 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
27797 /* Converting from client format to device format. */
27798 ma_data_converter_config converterConfig = ma_data_converter_config_init_default();
27799 converterConfig.formatIn = pDevice->playback.format;
27800 converterConfig.channelsIn = pDevice->playback.channels;
27801 converterConfig.sampleRateIn = pDevice->sampleRate;
27802 ma_channel_map_copy(converterConfig.channelMapIn, pDevice->playback.channelMap, pDevice->playback.channels);
27803 converterConfig.formatOut = pDevice->playback.internalFormat;
27804 converterConfig.channelsOut = pDevice->playback.internalChannels;
27805 converterConfig.sampleRateOut = pDevice->playback.internalSampleRate;
27806 ma_channel_map_copy(converterConfig.channelMapOut, pDevice->playback.internalChannelMap, pDevice->playback.internalChannels);
27807 converterConfig.resampling.allowDynamicSampleRate = MA_FALSE;
27808 converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
27809 converterConfig.resampling.linear.lpfCount = pDevice->resampling.linear.lpfCount;
27810 converterConfig.resampling.speex.quality = pDevice->resampling.speex.quality;
27811
27812 result = ma_data_converter_init(&converterConfig, &pDevice->playback.converter);
27813 if (result != MA_SUCCESS) {
27814 return result;
27815 }
27816 }
27817
27818 return MA_SUCCESS;
27819}
27820
27821
27822static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
27823{
27824 ma_device* pDevice = (ma_device*)pData;
27825 MA_ASSERT(pDevice != NULL);
27826
27827#ifdef MA_WIN32
27828 ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE);
27829#endif
27830
27831 /*
27832 When the device is being initialized it's initial state is set to MA_STATE_UNINITIALIZED. Before returning from
27833 ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately
27834 after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker
27835 thread to signal an event to know when the worker thread is ready for action.
27836 */
27837 ma_device__set_state(pDevice, MA_STATE_STOPPED);
27838 ma_event_signal(&pDevice->stopEvent);
27839
27840 for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */
27841 ma_stop_proc onStop;
27842
27843 /* We wait on an event to know when something has requested that the device be started and the main loop entered. */
27844 ma_event_wait(&pDevice->wakeupEvent);
27845
27846 /* Default result code. */
27847 pDevice->workResult = MA_SUCCESS;
27848
27849 /* If the reason for the wake up is that we are terminating, just break from the loop. */
27850 if (ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED) {
27851 break;
27852 }
27853
27854 /*
27855 Getting to this point means the device is wanting to get started. The function that has requested that the device
27856 be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event
27857 in both the success and error case. It's important that the state of the device is set _before_ signaling the event.
27858 */
27859 MA_ASSERT(ma_device__get_state(pDevice) == MA_STATE_STARTING);
27860
27861 /* Make sure the state is set appropriately. */
27862 ma_device__set_state(pDevice, MA_STATE_STARTED);
27863 ma_event_signal(&pDevice->startEvent);
27864
27865 if (pDevice->pContext->onDeviceMainLoop != NULL) {
27866 pDevice->pContext->onDeviceMainLoop(pDevice);
27867 } else {
27868 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "No main loop implementation.", MA_API_NOT_FOUND);
27869 }
27870
27871 /*
27872 Getting here means we have broken from the main loop which happens the application has requested that device be stopped. Note that this
27873 may have actually already happened above if the device was lost and miniaudio has attempted to re-initialize the device. In this case we
27874 don't want to be doing this a second time.
27875 */
27876 if (ma_device__get_state(pDevice) != MA_STATE_UNINITIALIZED) {
27877 if (pDevice->pContext->onDeviceStop) {
27878 pDevice->pContext->onDeviceStop(pDevice);
27879 }
27880 }
27881
27882 /* After the device has stopped, make sure an event is posted. */
27883 onStop = pDevice->onStop;
27884 if (onStop) {
27885 onStop(pDevice);
27886 }
27887
27888 /*
27889 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
27890 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
27891 uninitialized state to stopped state.
27892 */
27893 if (ma_device__get_state(pDevice) != MA_STATE_UNINITIALIZED) {
27894 ma_device__set_state(pDevice, MA_STATE_STOPPED);
27895 ma_event_signal(&pDevice->stopEvent);
27896 }
27897 }
27898
27899 /* Make sure we aren't continuously waiting on a stop event. */
27900 ma_event_signal(&pDevice->stopEvent); /* <-- Is this still needed? */
27901
27902#ifdef MA_WIN32
27903 ma_CoUninitialize(pDevice->pContext);
27904#endif
27905
27906 return (ma_thread_result)0;
27907}
27908
27909
27910/* Helper for determining whether or not the given device is initialized. */
27911static ma_bool32 ma_device__is_initialized(ma_device* pDevice)
27912{
27913 if (pDevice == NULL) {
27914 return MA_FALSE;
27915 }
27916
27917 return ma_device__get_state(pDevice) != MA_STATE_UNINITIALIZED;
27918}
27919
27920
27921#ifdef MA_WIN32
27922static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext)
27923{
27924 ma_CoUninitialize(pContext);
27925 ma_dlclose(pContext, pContext->win32.hUser32DLL);
27926 ma_dlclose(pContext, pContext->win32.hOle32DLL);
27927 ma_dlclose(pContext, pContext->win32.hAdvapi32DLL);
27928
27929 return MA_SUCCESS;
27930}
27931
27932static ma_result ma_context_init_backend_apis__win32(ma_context* pContext)
27933{
27934#ifdef MA_WIN32_DESKTOP
27935 /* Ole32.dll */
27936 pContext->win32.hOle32DLL = ma_dlopen(pContext, "ole32.dll");
27937 if (pContext->win32.hOle32DLL == NULL) {
27938 return MA_FAILED_TO_INIT_BACKEND;
27939 }
27940
27941 pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitializeEx");
27942 pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoUninitialize");
27943 pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoCreateInstance");
27944 pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoTaskMemFree");
27945 pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "PropVariantClear");
27946 pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "StringFromGUID2");
27947
27948
27949 /* User32.dll */
27950 pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll");
27951 if (pContext->win32.hUser32DLL == NULL) {
27952 return MA_FAILED_TO_INIT_BACKEND;
27953 }
27954
27955 pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow");
27956 pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow");
27957
27958
27959 /* Advapi32.dll */
27960 pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll");
27961 if (pContext->win32.hAdvapi32DLL == NULL) {
27962 return MA_FAILED_TO_INIT_BACKEND;
27963 }
27964
27965 pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA");
27966 pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey");
27967 pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA");
27968#endif
27969
27970 ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE);
27971 return MA_SUCCESS;
27972}
27973#else
27974static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext)
27975{
27976#if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING)
27977 ma_dlclose(pContext, pContext->posix.pthreadSO);
27978#else
27979 (void)pContext;
27980#endif
27981
27982 return MA_SUCCESS;
27983}
27984
27985static ma_result ma_context_init_backend_apis__nix(ma_context* pContext)
27986{
27987 /* pthread */
27988#if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING)
27989 const char* libpthreadFileNames[] = {
27990 "libpthread.so",
27991 "libpthread.so.0",
27992 "libpthread.dylib"
27993 };
27994 size_t i;
27995
27996 for (i = 0; i < sizeof(libpthreadFileNames) / sizeof(libpthreadFileNames[0]); ++i) {
27997 pContext->posix.pthreadSO = ma_dlopen(pContext, libpthreadFileNames[i]);
27998 if (pContext->posix.pthreadSO != NULL) {
27999 break;
28000 }
28001 }
28002
28003 if (pContext->posix.pthreadSO == NULL) {
28004 return MA_FAILED_TO_INIT_BACKEND;
28005 }
28006
28007 pContext->posix.pthread_create = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_create");
28008 pContext->posix.pthread_join = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_join");
28009 pContext->posix.pthread_mutex_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_init");
28010 pContext->posix.pthread_mutex_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_destroy");
28011 pContext->posix.pthread_mutex_lock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_lock");
28012 pContext->posix.pthread_mutex_unlock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_unlock");
28013 pContext->posix.pthread_cond_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_init");
28014 pContext->posix.pthread_cond_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_destroy");
28015 pContext->posix.pthread_cond_wait = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_wait");
28016 pContext->posix.pthread_cond_signal = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_signal");
28017 pContext->posix.pthread_attr_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_init");
28018 pContext->posix.pthread_attr_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_destroy");
28019 pContext->posix.pthread_attr_setschedpolicy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedpolicy");
28020 pContext->posix.pthread_attr_getschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_getschedparam");
28021 pContext->posix.pthread_attr_setschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedparam");
28022#else
28023 pContext->posix.pthread_create = (ma_proc)pthread_create;
28024 pContext->posix.pthread_join = (ma_proc)pthread_join;
28025 pContext->posix.pthread_mutex_init = (ma_proc)pthread_mutex_init;
28026 pContext->posix.pthread_mutex_destroy = (ma_proc)pthread_mutex_destroy;
28027 pContext->posix.pthread_mutex_lock = (ma_proc)pthread_mutex_lock;
28028 pContext->posix.pthread_mutex_unlock = (ma_proc)pthread_mutex_unlock;
28029 pContext->posix.pthread_cond_init = (ma_proc)pthread_cond_init;
28030 pContext->posix.pthread_cond_destroy = (ma_proc)pthread_cond_destroy;
28031 pContext->posix.pthread_cond_wait = (ma_proc)pthread_cond_wait;
28032 pContext->posix.pthread_cond_signal = (ma_proc)pthread_cond_signal;
28033 pContext->posix.pthread_attr_init = (ma_proc)pthread_attr_init;
28034 pContext->posix.pthread_attr_destroy = (ma_proc)pthread_attr_destroy;
28035#if !defined(__EMSCRIPTEN__)
28036 pContext->posix.pthread_attr_setschedpolicy = (ma_proc)pthread_attr_setschedpolicy;
28037 pContext->posix.pthread_attr_getschedparam = (ma_proc)pthread_attr_getschedparam;
28038 pContext->posix.pthread_attr_setschedparam = (ma_proc)pthread_attr_setschedparam;
28039#endif
28040#endif
28041
28042 return MA_SUCCESS;
28043}
28044#endif
28045
28046static ma_result ma_context_init_backend_apis(ma_context* pContext)
28047{
28048 ma_result result;
28049#ifdef MA_WIN32
28050 result = ma_context_init_backend_apis__win32(pContext);
28051#else
28052 result = ma_context_init_backend_apis__nix(pContext);
28053#endif
28054
28055 return result;
28056}
28057
28058static ma_result ma_context_uninit_backend_apis(ma_context* pContext)
28059{
28060 ma_result result;
28061#ifdef MA_WIN32
28062 result = ma_context_uninit_backend_apis__win32(pContext);
28063#else
28064 result = ma_context_uninit_backend_apis__nix(pContext);
28065#endif
28066
28067 return result;
28068}
28069
28070
28071static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext)
28072{
28073 return pContext->isBackendAsynchronous;
28074}
28075
28076
28077ma_context_config ma_context_config_init()
28078{
28079 ma_context_config config;
28080 MA_ZERO_OBJECT(&config);
28081
28082 return config;
28083}
28084
28085ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext)
28086{
28087 ma_result result;
28088 ma_context_config config;
28089 ma_backend defaultBackends[ma_backend_null+1];
28090 ma_uint32 iBackend;
28091 ma_backend* pBackendsToIterate;
28092 ma_uint32 backendsToIterateCount;
28093
28094 if (pContext == NULL) {
28095 return MA_INVALID_ARGS;
28096 }
28097
28098 MA_ZERO_OBJECT(pContext);
28099
28100 /* Always make sure the config is set first to ensure properties are available as soon as possible. */
28101 if (pConfig != NULL) {
28102 config = *pConfig;
28103 } else {
28104 config = ma_context_config_init();
28105 }
28106
28107 pContext->logCallback = config.logCallback;
28108 pContext->threadPriority = config.threadPriority;
28109 pContext->pUserData = config.pUserData;
28110
28111 result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &config.allocationCallbacks);
28112 if (result != MA_SUCCESS) {
28113 return result;
28114 }
28115
28116 /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */
28117 result = ma_context_init_backend_apis(pContext);
28118 if (result != MA_SUCCESS) {
28119 return result;
28120 }
28121
28122 for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
28123 defaultBackends[iBackend] = (ma_backend)iBackend;
28124 }
28125
28126 pBackendsToIterate = (ma_backend*)backends;
28127 backendsToIterateCount = backendCount;
28128 if (pBackendsToIterate == NULL) {
28129 pBackendsToIterate = (ma_backend*)defaultBackends;
28130 backendsToIterateCount = ma_countof(defaultBackends);
28131 }
28132
28133 MA_ASSERT(pBackendsToIterate != NULL);
28134
28135 for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
28136 ma_backend backend = pBackendsToIterate[iBackend];
28137
28138 result = MA_NO_BACKEND;
28139 switch (backend) {
28140 #ifdef MA_HAS_WASAPI
28141 case ma_backend_wasapi:
28142 {
28143 result = ma_context_init__wasapi(&config, pContext);
28144 } break;
28145 #endif
28146 #ifdef MA_HAS_DSOUND
28147 case ma_backend_dsound:
28148 {
28149 result = ma_context_init__dsound(&config, pContext);
28150 } break;
28151 #endif
28152 #ifdef MA_HAS_WINMM
28153 case ma_backend_winmm:
28154 {
28155 result = ma_context_init__winmm(&config, pContext);
28156 } break;
28157 #endif
28158 #ifdef MA_HAS_ALSA
28159 case ma_backend_alsa:
28160 {
28161 result = ma_context_init__alsa(&config, pContext);
28162 } break;
28163 #endif
28164 #ifdef MA_HAS_PULSEAUDIO
28165 case ma_backend_pulseaudio:
28166 {
28167 result = ma_context_init__pulse(&config, pContext);
28168 } break;
28169 #endif
28170 #ifdef MA_HAS_JACK
28171 case ma_backend_jack:
28172 {
28173 result = ma_context_init__jack(&config, pContext);
28174 } break;
28175 #endif
28176 #ifdef MA_HAS_COREAUDIO
28177 case ma_backend_coreaudio:
28178 {
28179 result = ma_context_init__coreaudio(&config, pContext);
28180 } break;
28181 #endif
28182 #ifdef MA_HAS_SNDIO
28183 case ma_backend_sndio:
28184 {
28185 result = ma_context_init__sndio(&config, pContext);
28186 } break;
28187 #endif
28188 #ifdef MA_HAS_AUDIO4
28189 case ma_backend_audio4:
28190 {
28191 result = ma_context_init__audio4(&config, pContext);
28192 } break;
28193 #endif
28194 #ifdef MA_HAS_OSS
28195 case ma_backend_oss:
28196 {
28197 result = ma_context_init__oss(&config, pContext);
28198 } break;
28199 #endif
28200 #ifdef MA_HAS_AAUDIO
28201 case ma_backend_aaudio:
28202 {
28203 result = ma_context_init__aaudio(&config, pContext);
28204 } break;
28205 #endif
28206 #ifdef MA_HAS_OPENSL
28207 case ma_backend_opensl:
28208 {
28209 result = ma_context_init__opensl(&config, pContext);
28210 } break;
28211 #endif
28212 #ifdef MA_HAS_WEBAUDIO
28213 case ma_backend_webaudio:
28214 {
28215 result = ma_context_init__webaudio(&config, pContext);
28216 } break;
28217 #endif
28218 #ifdef MA_HAS_NULL
28219 case ma_backend_null:
28220 {
28221 result = ma_context_init__null(&config, pContext);
28222 } break;
28223 #endif
28224
28225 default: break;
28226 }
28227
28228 /* If this iteration was successful, return. */
28229 if (result == MA_SUCCESS) {
28230 result = ma_mutex_init(pContext, &pContext->deviceEnumLock);
28231 if (result != MA_SUCCESS) {
28232 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.", MA_FAILED_TO_CREATE_MUTEX);
28233 }
28234 result = ma_mutex_init(pContext, &pContext->deviceInfoLock);
28235 if (result != MA_SUCCESS) {
28236 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.", MA_FAILED_TO_CREATE_MUTEX);
28237 }
28238
28239#ifdef MA_DEBUG_OUTPUT
28240 printf("[miniaudio] Endian: %s\n", ma_is_little_endian() ? "LE" : "BE");
28241 printf("[miniaudio] SSE2: %s\n", ma_has_sse2() ? "YES" : "NO");
28242 printf("[miniaudio] AVX2: %s\n", ma_has_avx2() ? "YES" : "NO");
28243 printf("[miniaudio] AVX512F: %s\n", ma_has_avx512f() ? "YES" : "NO");
28244 printf("[miniaudio] NEON: %s\n", ma_has_neon() ? "YES" : "NO");
28245#endif
28246
28247 pContext->backend = backend;
28248 return result;
28249 }
28250 }
28251
28252 /* If we get here it means an error occurred. */
28253 MA_ZERO_OBJECT(pContext); /* Safety. */
28254 return MA_NO_BACKEND;
28255}
28256
28257ma_result ma_context_uninit(ma_context* pContext)
28258{
28259 if (pContext == NULL) {
28260 return MA_INVALID_ARGS;
28261 }
28262
28263 pContext->onUninit(pContext);
28264
28265 ma_mutex_uninit(&pContext->deviceEnumLock);
28266 ma_mutex_uninit(&pContext->deviceInfoLock);
28267 ma__free_from_callbacks(pContext->pDeviceInfos, &pContext->allocationCallbacks);
28268 ma_context_uninit_backend_apis(pContext);
28269
28270 return MA_SUCCESS;
28271}
28272
28273
28274ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
28275{
28276 ma_result result;
28277
28278 if (pContext == NULL || pContext->onEnumDevices == NULL || callback == NULL) {
28279 return MA_INVALID_ARGS;
28280 }
28281
28282 ma_mutex_lock(&pContext->deviceEnumLock);
28283 {
28284 result = pContext->onEnumDevices(pContext, callback, pUserData);
28285 }
28286 ma_mutex_unlock(&pContext->deviceEnumLock);
28287
28288 return result;
28289}
28290
28291
28292static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
28293{
28294 /*
28295 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
28296 it's just appended to the end. If it's a playback device it's inserted just before the first capture device.
28297 */
28298
28299 /*
28300 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
28301 simple fixed size increment for buffer expansion.
28302 */
28303 const ma_uint32 bufferExpansionCount = 2;
28304 const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount;
28305
28306 if (pContext->deviceInfoCapacity >= totalDeviceInfoCount) {
28307 ma_uint32 oldCapacity = pContext->deviceInfoCapacity;
28308 ma_uint32 newCapacity = oldCapacity + bufferExpansionCount;
28309 ma_device_info* pNewInfos = (ma_device_info*)ma__realloc_from_callbacks(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, sizeof(*pContext->pDeviceInfos)*oldCapacity, &pContext->allocationCallbacks);
28310 if (pNewInfos == NULL) {
28311 return MA_FALSE; /* Out of memory. */
28312 }
28313
28314 pContext->pDeviceInfos = pNewInfos;
28315 pContext->deviceInfoCapacity = newCapacity;
28316 }
28317
28318 if (deviceType == ma_device_type_playback) {
28319 /* Playback. Insert just before the first capture device. */
28320
28321 /* The first thing to do is move all of the capture devices down a slot. */
28322 ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount;
28323 size_t iCaptureDevice;
28324 for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) {
28325 pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1];
28326 }
28327
28328 /* Now just insert where the first capture device was before moving it down a slot. */
28329 pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo;
28330 pContext->playbackDeviceInfoCount += 1;
28331 } else {
28332 /* Capture. Insert at the end. */
28333 pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo;
28334 pContext->captureDeviceInfoCount += 1;
28335 }
28336
28337 (void)pUserData;
28338 return MA_TRUE;
28339}
28340
28341ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount)
28342{
28343 ma_result result;
28344
28345 /* Safety. */
28346 if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL;
28347 if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0;
28348 if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL;
28349 if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0;
28350
28351 if (pContext == NULL || pContext->onEnumDevices == NULL) {
28352 return MA_INVALID_ARGS;
28353 }
28354
28355 /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */
28356 ma_mutex_lock(&pContext->deviceEnumLock);
28357 {
28358 /* Reset everything first. */
28359 pContext->playbackDeviceInfoCount = 0;
28360 pContext->captureDeviceInfoCount = 0;
28361
28362 /* Now enumerate over available devices. */
28363 result = pContext->onEnumDevices(pContext, ma_context_get_devices__enum_callback, NULL);
28364 if (result == MA_SUCCESS) {
28365 /* Playback devices. */
28366 if (ppPlaybackDeviceInfos != NULL) {
28367 *ppPlaybackDeviceInfos = pContext->pDeviceInfos;
28368 }
28369 if (pPlaybackDeviceCount != NULL) {
28370 *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount;
28371 }
28372
28373 /* Capture devices. */
28374 if (ppCaptureDeviceInfos != NULL) {
28375 *ppCaptureDeviceInfos = pContext->pDeviceInfos + pContext->playbackDeviceInfoCount; /* Capture devices come after playback devices. */
28376 }
28377 if (pCaptureDeviceCount != NULL) {
28378 *pCaptureDeviceCount = pContext->captureDeviceInfoCount;
28379 }
28380 }
28381 }
28382 ma_mutex_unlock(&pContext->deviceEnumLock);
28383
28384 return result;
28385}
28386
28387ma_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)
28388{
28389 ma_device_info deviceInfo;
28390
28391 /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */
28392 if (pContext == NULL || pDeviceInfo == NULL) {
28393 return MA_INVALID_ARGS;
28394 }
28395
28396 MA_ZERO_OBJECT(&deviceInfo);
28397
28398 /* Help the backend out by copying over the device ID if we have one. */
28399 if (pDeviceID != NULL) {
28400 MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID));
28401 }
28402
28403 /* The backend may have an optimized device info retrieval function. If so, try that first. */
28404 if (pContext->onGetDeviceInfo != NULL) {
28405 ma_result result;
28406 ma_mutex_lock(&pContext->deviceInfoLock);
28407 {
28408 result = pContext->onGetDeviceInfo(pContext, deviceType, pDeviceID, shareMode, &deviceInfo);
28409 }
28410 ma_mutex_unlock(&pContext->deviceInfoLock);
28411
28412 /* Clamp ranges. */
28413 deviceInfo.minChannels = ma_max(deviceInfo.minChannels, MA_MIN_CHANNELS);
28414 deviceInfo.maxChannels = ma_min(deviceInfo.maxChannels, MA_MAX_CHANNELS);
28415 deviceInfo.minSampleRate = ma_max(deviceInfo.minSampleRate, MA_MIN_SAMPLE_RATE);
28416 deviceInfo.maxSampleRate = ma_min(deviceInfo.maxSampleRate, MA_MAX_SAMPLE_RATE);
28417
28418 *pDeviceInfo = deviceInfo;
28419 return result;
28420 }
28421
28422 /* Getting here means onGetDeviceInfo has not been set. */
28423 return MA_ERROR;
28424}
28425
28426ma_bool32 ma_context_is_loopback_supported(ma_context* pContext)
28427{
28428 if (pContext == NULL) {
28429 return MA_FALSE;
28430 }
28431
28432 return ma_is_loopback_supported(pContext->backend);
28433}
28434
28435
28436ma_device_config ma_device_config_init(ma_device_type deviceType)
28437{
28438 ma_device_config config;
28439 MA_ZERO_OBJECT(&config);
28440 config.deviceType = deviceType;
28441
28442 /* Resampling defaults. We must never use the Speex backend by default because it uses licensed third party code. */
28443 config.resampling.algorithm = ma_resample_algorithm_linear;
28444 config.resampling.linear.lpfCount = ma_min(MA_DEFAULT_RESAMPLER_LPF_FILTERS, MA_MAX_RESAMPLER_LPF_FILTERS);
28445 config.resampling.speex.quality = 3;
28446
28447 return config;
28448}
28449
28450ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
28451{
28452 ma_result result;
28453 ma_device_config config;
28454
28455 if (pContext == NULL) {
28456 return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice);
28457 }
28458 if (pDevice == NULL) {
28459 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
28460 }
28461 if (pConfig == NULL) {
28462 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pConfig == NULL).", MA_INVALID_ARGS);
28463 }
28464
28465 /* We need to make a copy of the config so we can set default values if they were left unset in the input config. */
28466 config = *pConfig;
28467
28468 /* Basic config validation. */
28469 if (config.deviceType != ma_device_type_playback && config.deviceType != ma_device_type_capture && config.deviceType != ma_device_type_duplex && config.deviceType != ma_device_type_loopback) {
28470 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);
28471 }
28472
28473 if (config.deviceType == ma_device_type_capture || config.deviceType == ma_device_type_duplex) {
28474 if (config.capture.channels > MA_MAX_CHANNELS) {
28475 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);
28476 }
28477 if (!ma__is_channel_map_valid(config.capture.channelMap, config.capture.channels)) {
28478 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);
28479 }
28480 }
28481
28482 if (config.deviceType == ma_device_type_playback || config.deviceType == ma_device_type_duplex || config.deviceType == ma_device_type_loopback) {
28483 if (config.playback.channels > MA_MAX_CHANNELS) {
28484 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);
28485 }
28486 if (!ma__is_channel_map_valid(config.playback.channelMap, config.playback.channels)) {
28487 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);
28488 }
28489 }
28490
28491
28492 MA_ZERO_OBJECT(pDevice);
28493 pDevice->pContext = pContext;
28494
28495 /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */
28496 pDevice->pUserData = config.pUserData;
28497 pDevice->onData = config.dataCallback;
28498 pDevice->onStop = config.stopCallback;
28499
28500 if (((ma_uintptr)pDevice % sizeof(pDevice)) != 0) {
28501 if (pContext->logCallback) {
28502 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.");
28503 }
28504 }
28505
28506 pDevice->noPreZeroedOutputBuffer = config.noPreZeroedOutputBuffer;
28507 pDevice->noClip = config.noClip;
28508 pDevice->masterVolumeFactor = 1;
28509
28510 /*
28511 When passing in 0 for the format/channels/rate/chmap it means the device will be using whatever is chosen by the backend. If everything is set
28512 to defaults it means the format conversion pipeline will run on a fast path where data transfer is just passed straight through to the backend.
28513 */
28514 if (config.sampleRate == 0) {
28515 config.sampleRate = MA_DEFAULT_SAMPLE_RATE;
28516 pDevice->usingDefaultSampleRate = MA_TRUE;
28517 }
28518
28519 if (config.capture.format == ma_format_unknown) {
28520 config.capture.format = MA_DEFAULT_FORMAT;
28521 pDevice->capture.usingDefaultFormat = MA_TRUE;
28522 }
28523 if (config.capture.channels == 0) {
28524 config.capture.channels = MA_DEFAULT_CHANNELS;
28525 pDevice->capture.usingDefaultChannels = MA_TRUE;
28526 }
28527 if (config.capture.channelMap[0] == MA_CHANNEL_NONE) {
28528 pDevice->capture.usingDefaultChannelMap = MA_TRUE;
28529 }
28530
28531 if (config.playback.format == ma_format_unknown) {
28532 config.playback.format = MA_DEFAULT_FORMAT;
28533 pDevice->playback.usingDefaultFormat = MA_TRUE;
28534 }
28535 if (config.playback.channels == 0) {
28536 config.playback.channels = MA_DEFAULT_CHANNELS;
28537 pDevice->playback.usingDefaultChannels = MA_TRUE;
28538 }
28539 if (config.playback.channelMap[0] == MA_CHANNEL_NONE) {
28540 pDevice->playback.usingDefaultChannelMap = MA_TRUE;
28541 }
28542
28543
28544 /* Default periods. */
28545 if (config.periods == 0) {
28546 config.periods = MA_DEFAULT_PERIODS;
28547 pDevice->usingDefaultPeriods = MA_TRUE;
28548 }
28549
28550 /*
28551 Must have at least 3 periods for full-duplex mode. The idea is that the playback and capture positions hang out in the middle period, with the surrounding
28552 periods acting as a buffer in case the capture and playback devices get's slightly out of sync.
28553 */
28554 if (config.deviceType == ma_device_type_duplex && config.periods < 3) {
28555 config.periods = 3;
28556 }
28557
28558 /* Default buffer size. */
28559 if (config.periodSizeInMilliseconds == 0 && config.periodSizeInFrames == 0) {
28560 config.periodSizeInMilliseconds = (config.performanceProfile == ma_performance_profile_low_latency) ? MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY : MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE;
28561 pDevice->usingDefaultBufferSize = MA_TRUE;
28562 }
28563
28564
28565
28566 pDevice->type = config.deviceType;
28567 pDevice->sampleRate = config.sampleRate;
28568 pDevice->resampling.algorithm = config.resampling.algorithm;
28569 pDevice->resampling.linear.lpfCount = config.resampling.linear.lpfCount;
28570 pDevice->resampling.speex.quality = config.resampling.speex.quality;
28571
28572 pDevice->capture.shareMode = config.capture.shareMode;
28573 pDevice->capture.format = config.capture.format;
28574 pDevice->capture.channels = config.capture.channels;
28575 ma_channel_map_copy(pDevice->capture.channelMap, config.capture.channelMap, config.capture.channels);
28576
28577 pDevice->playback.shareMode = config.playback.shareMode;
28578 pDevice->playback.format = config.playback.format;
28579 pDevice->playback.channels = config.playback.channels;
28580 ma_channel_map_copy(pDevice->playback.channelMap, config.playback.channelMap, config.playback.channels);
28581
28582
28583 /* The internal format, channel count and sample rate can be modified by the backend. */
28584 pDevice->capture.internalFormat = pDevice->capture.format;
28585 pDevice->capture.internalChannels = pDevice->capture.channels;
28586 pDevice->capture.internalSampleRate = pDevice->sampleRate;
28587 ma_channel_map_copy(pDevice->capture.internalChannelMap, pDevice->capture.channelMap, pDevice->capture.channels);
28588
28589 pDevice->playback.internalFormat = pDevice->playback.format;
28590 pDevice->playback.internalChannels = pDevice->playback.channels;
28591 pDevice->playback.internalSampleRate = pDevice->sampleRate;
28592 ma_channel_map_copy(pDevice->playback.internalChannelMap, pDevice->playback.channelMap, pDevice->playback.channels);
28593
28594
28595 if (ma_mutex_init(pContext, &pDevice->lock) != MA_SUCCESS) {
28596 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create mutex.", MA_FAILED_TO_CREATE_MUTEX);
28597 }
28598
28599 /*
28600 When the device is started, the worker thread is the one that does the actual startup of the backend device. We
28601 use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device.
28602
28603 Each of these semaphores is released internally by the worker thread when the work is completed. The start
28604 semaphore is also used to wake up the worker thread.
28605 */
28606 if (ma_event_init(pContext, &pDevice->wakeupEvent) != MA_SUCCESS) {
28607 ma_mutex_uninit(&pDevice->lock);
28608 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread wakeup event.", MA_FAILED_TO_CREATE_EVENT);
28609 }
28610 if (ma_event_init(pContext, &pDevice->startEvent) != MA_SUCCESS) {
28611 ma_event_uninit(&pDevice->wakeupEvent);
28612 ma_mutex_uninit(&pDevice->lock);
28613 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread start event.", MA_FAILED_TO_CREATE_EVENT);
28614 }
28615 if (ma_event_init(pContext, &pDevice->stopEvent) != MA_SUCCESS) {
28616 ma_event_uninit(&pDevice->startEvent);
28617 ma_event_uninit(&pDevice->wakeupEvent);
28618 ma_mutex_uninit(&pDevice->lock);
28619 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread stop event.", MA_FAILED_TO_CREATE_EVENT);
28620 }
28621
28622
28623 result = pContext->onDeviceInit(pContext, &config, pDevice);
28624 if (result != MA_SUCCESS) {
28625 return result;
28626 }
28627
28628 ma_device__post_init_setup(pDevice, pConfig->deviceType);
28629
28630
28631 /* If the backend did not fill out a name for the device, try a generic method. */
28632 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28633 if (pDevice->capture.name[0] == '\0') {
28634 if (ma_context__try_get_device_name_by_id(pContext, ma_device_type_capture, config.capture.pDeviceID, pDevice->capture.name, sizeof(pDevice->capture.name)) != MA_SUCCESS) {
28635 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), (config.capture.pDeviceID == NULL) ? MA_DEFAULT_CAPTURE_DEVICE_NAME : "Capture Device", (size_t)-1);
28636 }
28637 }
28638 }
28639 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
28640 if (pDevice->playback.name[0] == '\0') {
28641 if (ma_context__try_get_device_name_by_id(pContext, ma_device_type_playback, config.playback.pDeviceID, pDevice->playback.name, sizeof(pDevice->playback.name)) != MA_SUCCESS) {
28642 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), (config.playback.pDeviceID == NULL) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : "Playback Device", (size_t)-1);
28643 }
28644 }
28645 }
28646
28647
28648 /* Some backends don't require the worker thread. */
28649 if (!ma_context_is_backend_asynchronous(pContext)) {
28650 /* The worker thread. */
28651 if (ma_thread_create(pContext, &pDevice->thread, ma_worker_thread, pDevice) != MA_SUCCESS) {
28652 ma_device_uninit(pDevice);
28653 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread.", MA_FAILED_TO_CREATE_THREAD);
28654 }
28655
28656 /* Wait for the worker thread to put the device into it's stopped state for real. */
28657 ma_event_wait(&pDevice->stopEvent);
28658 } else {
28659 ma_device__set_state(pDevice, MA_STATE_STOPPED);
28660 }
28661
28662
28663#ifdef MA_DEBUG_OUTPUT
28664 printf("[%s]\n", ma_get_backend_name(pDevice->pContext->backend));
28665 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28666 printf(" %s (%s)\n", pDevice->capture.name, "Capture");
28667 printf(" Format: %s -> %s\n", ma_get_format_name(pDevice->capture.format), ma_get_format_name(pDevice->capture.internalFormat));
28668 printf(" Channels: %d -> %d\n", pDevice->capture.channels, pDevice->capture.internalChannels);
28669 printf(" Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->capture.internalSampleRate);
28670 printf(" Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods));
28671 printf(" Conversion:\n");
28672 printf(" Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO");
28673 printf(" Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO");
28674 printf(" Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO");
28675 printf(" Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO");
28676 printf(" Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO");
28677 }
28678 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
28679 printf(" %s (%s)\n", pDevice->playback.name, "Playback");
28680 printf(" Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat));
28681 printf(" Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels);
28682 printf(" Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate);
28683 printf(" Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods));
28684 printf(" Conversion:\n");
28685 printf(" Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO");
28686 printf(" Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO");
28687 printf(" Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO");
28688 printf(" Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO");
28689 printf(" Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO");
28690 }
28691#endif
28692
28693
28694 MA_ASSERT(ma_device__get_state(pDevice) == MA_STATE_STOPPED);
28695 return MA_SUCCESS;
28696}
28697
28698ma_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)
28699{
28700 ma_result result;
28701 ma_context* pContext;
28702 ma_backend defaultBackends[ma_backend_null+1];
28703 ma_uint32 iBackend;
28704 ma_backend* pBackendsToIterate;
28705 ma_uint32 backendsToIterateCount;
28706 ma_allocation_callbacks allocationCallbacks;
28707
28708 if (pConfig == NULL) {
28709 return MA_INVALID_ARGS;
28710 }
28711
28712 if (pContextConfig != NULL) {
28713 result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks);
28714 if (result != MA_SUCCESS) {
28715 return result;
28716 }
28717 } else {
28718 allocationCallbacks = ma_allocation_callbacks_init_default();
28719 }
28720
28721
28722 pContext = (ma_context*)ma__malloc_from_callbacks(sizeof(*pContext), &allocationCallbacks);
28723 if (pContext == NULL) {
28724 return MA_OUT_OF_MEMORY;
28725 }
28726
28727 for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
28728 defaultBackends[iBackend] = (ma_backend)iBackend;
28729 }
28730
28731 pBackendsToIterate = (ma_backend*)backends;
28732 backendsToIterateCount = backendCount;
28733 if (pBackendsToIterate == NULL) {
28734 pBackendsToIterate = (ma_backend*)defaultBackends;
28735 backendsToIterateCount = ma_countof(defaultBackends);
28736 }
28737
28738 result = MA_NO_BACKEND;
28739
28740 for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
28741 result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext);
28742 if (result == MA_SUCCESS) {
28743 result = ma_device_init(pContext, pConfig, pDevice);
28744 if (result == MA_SUCCESS) {
28745 break; /* Success. */
28746 } else {
28747 ma_context_uninit(pContext); /* Failure. */
28748 }
28749 }
28750 }
28751
28752 if (result != MA_SUCCESS) {
28753 ma__free_from_callbacks(pContext, &allocationCallbacks);
28754 return result;
28755 }
28756
28757 pDevice->isOwnerOfContext = MA_TRUE;
28758 return result;
28759}
28760
28761void ma_device_uninit(ma_device* pDevice)
28762{
28763 if (!ma_device__is_initialized(pDevice)) {
28764 return;
28765 }
28766
28767 /* 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. */
28768 if (ma_device_is_started(pDevice)) {
28769 ma_device_stop(pDevice);
28770 }
28771
28772 /* Putting the device into an uninitialized state will make the worker thread return. */
28773 ma_device__set_state(pDevice, MA_STATE_UNINITIALIZED);
28774
28775 /* Wake up the worker thread and wait for it to properly terminate. */
28776 if (!ma_context_is_backend_asynchronous(pDevice->pContext)) {
28777 ma_event_signal(&pDevice->wakeupEvent);
28778 ma_thread_wait(&pDevice->thread);
28779 }
28780
28781 pDevice->pContext->onDeviceUninit(pDevice);
28782
28783 ma_event_uninit(&pDevice->stopEvent);
28784 ma_event_uninit(&pDevice->startEvent);
28785 ma_event_uninit(&pDevice->wakeupEvent);
28786 ma_mutex_uninit(&pDevice->lock);
28787
28788 if (pDevice->isOwnerOfContext) {
28789 ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks;
28790
28791 ma_context_uninit(pDevice->pContext);
28792 ma__free_from_callbacks(pDevice->pContext, &allocationCallbacks);
28793 }
28794
28795 MA_ZERO_OBJECT(pDevice);
28796}
28797
28798ma_result ma_device_start(ma_device* pDevice)
28799{
28800 ma_result result;
28801
28802 if (pDevice == NULL) {
28803 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_start() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
28804 }
28805
28806 if (ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED) {
28807 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_start() called for an uninitialized device.", MA_DEVICE_NOT_INITIALIZED);
28808 }
28809
28810 if (ma_device__get_state(pDevice) == MA_STATE_STARTED) {
28811 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. */
28812 }
28813
28814 result = MA_ERROR;
28815 ma_mutex_lock(&pDevice->lock);
28816 {
28817 /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */
28818 MA_ASSERT(ma_device__get_state(pDevice) == MA_STATE_STOPPED);
28819
28820 ma_device__set_state(pDevice, MA_STATE_STARTING);
28821
28822 /* Asynchronous backends need to be handled differently. */
28823 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
28824 result = pDevice->pContext->onDeviceStart(pDevice);
28825 if (result == MA_SUCCESS) {
28826 ma_device__set_state(pDevice, MA_STATE_STARTED);
28827 }
28828 } else {
28829 /*
28830 Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the
28831 thread and then wait for the start event.
28832 */
28833 ma_event_signal(&pDevice->wakeupEvent);
28834
28835 /*
28836 Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device
28837 into the started state. Don't call ma_device__set_state() here.
28838 */
28839 ma_event_wait(&pDevice->startEvent);
28840 result = pDevice->workResult;
28841 }
28842 }
28843 ma_mutex_unlock(&pDevice->lock);
28844
28845 return result;
28846}
28847
28848ma_result ma_device_stop(ma_device* pDevice)
28849{
28850 ma_result result;
28851
28852 if (pDevice == NULL) {
28853 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_stop() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
28854 }
28855
28856 if (ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED) {
28857 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_stop() called for an uninitialized device.", MA_DEVICE_NOT_INITIALIZED);
28858 }
28859
28860 if (ma_device__get_state(pDevice) == MA_STATE_STOPPED) {
28861 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. */
28862 }
28863
28864 result = MA_ERROR;
28865 ma_mutex_lock(&pDevice->lock);
28866 {
28867 /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */
28868 MA_ASSERT(ma_device__get_state(pDevice) == MA_STATE_STARTED);
28869
28870 ma_device__set_state(pDevice, MA_STATE_STOPPING);
28871
28872 /* There's no need to wake up the thread like we do when starting. */
28873
28874 if (pDevice->pContext->onDeviceStop) {
28875 result = pDevice->pContext->onDeviceStop(pDevice);
28876 } else {
28877 result = MA_SUCCESS;
28878 }
28879
28880 /* Asynchronous backends need to be handled differently. */
28881 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
28882 ma_device__set_state(pDevice, MA_STATE_STOPPED);
28883 } else {
28884 /* Synchronous backends. */
28885
28886 /*
28887 We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be
28888 the one who puts the device into the stopped state. Don't call ma_device__set_state() here.
28889 */
28890 ma_event_wait(&pDevice->stopEvent);
28891 result = MA_SUCCESS;
28892 }
28893 }
28894 ma_mutex_unlock(&pDevice->lock);
28895
28896 return result;
28897}
28898
28899ma_bool32 ma_device_is_started(ma_device* pDevice)
28900{
28901 if (pDevice == NULL) {
28902 return MA_FALSE;
28903 }
28904
28905 return ma_device__get_state(pDevice) == MA_STATE_STARTED;
28906}
28907
28908ma_result ma_device_set_master_volume(ma_device* pDevice, float volume)
28909{
28910 if (pDevice == NULL) {
28911 return MA_INVALID_ARGS;
28912 }
28913
28914 if (volume < 0.0f || volume > 1.0f) {
28915 return MA_INVALID_ARGS;
28916 }
28917
28918 pDevice->masterVolumeFactor = volume;
28919
28920 return MA_SUCCESS;
28921}
28922
28923ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume)
28924{
28925 if (pVolume == NULL) {
28926 return MA_INVALID_ARGS;
28927 }
28928
28929 if (pDevice == NULL) {
28930 *pVolume = 0;
28931 return MA_INVALID_ARGS;
28932 }
28933
28934 *pVolume = pDevice->masterVolumeFactor;
28935
28936 return MA_SUCCESS;
28937}
28938
28939ma_result ma_device_set_master_gain_db(ma_device* pDevice, float gainDB)
28940{
28941 if (gainDB > 0) {
28942 return MA_INVALID_ARGS;
28943 }
28944
28945 return ma_device_set_master_volume(pDevice, ma_gain_db_to_factor(gainDB));
28946}
28947
28948ma_result ma_device_get_master_gain_db(ma_device* pDevice, float* pGainDB)
28949{
28950 float factor;
28951 ma_result result;
28952
28953 if (pGainDB == NULL) {
28954 return MA_INVALID_ARGS;
28955 }
28956
28957 result = ma_device_get_master_volume(pDevice, &factor);
28958 if (result != MA_SUCCESS) {
28959 *pGainDB = 0;
28960 return result;
28961 }
28962
28963 *pGainDB = ma_factor_to_gain_db(factor);
28964
28965 return MA_SUCCESS;
28966}
28967#endif /* MA_NO_DEVICE_IO */
28968
28969
28970/**************************************************************************************************************************************************************
28971
28972Biquad Filter
28973
28974**************************************************************************************************************************************************************/
28975#ifndef MA_BIQUAD_FIXED_POINT_SHIFT
28976#define MA_BIQUAD_FIXED_POINT_SHIFT 14
28977#endif
28978
28979static ma_int32 ma_biquad_float_to_fp(double x)
28980{
28981 return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT));
28982}
28983
28984ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2)
28985{
28986 ma_biquad_config config;
28987
28988 MA_ZERO_OBJECT(&config);
28989 config.format = format;
28990 config.channels = channels;
28991 config.b0 = b0;
28992 config.b1 = b1;
28993 config.b2 = b2;
28994 config.a0 = a0;
28995 config.a1 = a1;
28996 config.a2 = a2;
28997
28998 return config;
28999}
29000
29001ma_result ma_biquad_init(const ma_biquad_config* pConfig, ma_biquad* pBQ)
29002{
29003 if (pBQ == NULL) {
29004 return MA_INVALID_ARGS;
29005 }
29006
29007 MA_ZERO_OBJECT(pBQ);
29008
29009 if (pConfig == NULL) {
29010 return MA_INVALID_ARGS;
29011 }
29012
29013 return ma_biquad_reinit(pConfig, pBQ);
29014}
29015
29016ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ)
29017{
29018 if (pBQ == NULL || pConfig == NULL) {
29019 return MA_INVALID_ARGS;
29020 }
29021
29022 if (pConfig->a0 == 0) {
29023 return MA_INVALID_ARGS; /* Division by zero. */
29024 }
29025
29026 /* Only supporting f32 and s16. */
29027 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
29028 return MA_INVALID_ARGS;
29029 }
29030
29031 /* The format cannot be changed after initialization. */
29032 if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) {
29033 return MA_INVALID_OPERATION;
29034 }
29035
29036 /* The channel count cannot be changed after initialization. */
29037 if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) {
29038 return MA_INVALID_OPERATION;
29039 }
29040
29041
29042 pBQ->format = pConfig->format;
29043 pBQ->channels = pConfig->channels;
29044
29045 /* Normalize. */
29046 if (pConfig->format == ma_format_f32) {
29047 pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0);
29048 pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0);
29049 pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0);
29050 pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0);
29051 pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0);
29052 } else {
29053 pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0);
29054 pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0);
29055 pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0);
29056 pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0);
29057 pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0);
29058 }
29059
29060 return MA_SUCCESS;
29061}
29062
29063static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX)
29064{
29065 ma_uint32 c;
29066 const float b0 = pBQ->b0.f32;
29067 const float b1 = pBQ->b1.f32;
29068 const float b2 = pBQ->b2.f32;
29069 const float a1 = pBQ->a1.f32;
29070 const float a2 = pBQ->a2.f32;
29071
29072 for (c = 0; c < pBQ->channels; c += 1) {
29073 float r1 = pBQ->r1[c].f32;
29074 float r2 = pBQ->r2[c].f32;
29075 float x = pX[c];
29076 float y;
29077
29078 y = b0*x + r1;
29079 r1 = b1*x - a1*y + r2;
29080 r2 = b2*x - a2*y;
29081
29082 pY[c] = y;
29083 pBQ->r1[c].f32 = r1;
29084 pBQ->r2[c].f32 = r2;
29085 }
29086}
29087
29088static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX)
29089{
29090 ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
29091}
29092
29093static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
29094{
29095 ma_uint32 c;
29096 const ma_int32 b0 = pBQ->b0.s32;
29097 const ma_int32 b1 = pBQ->b1.s32;
29098 const ma_int32 b2 = pBQ->b2.s32;
29099 const ma_int32 a1 = pBQ->a1.s32;
29100 const ma_int32 a2 = pBQ->a2.s32;
29101
29102 for (c = 0; c < pBQ->channels; c += 1) {
29103 ma_int32 r1 = pBQ->r1[c].s32;
29104 ma_int32 r2 = pBQ->r2[c].s32;
29105 ma_int32 x = pX[c];
29106 ma_int32 y;
29107
29108 y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
29109 r1 = (b1*x - a1*y + r2);
29110 r2 = (b2*x - a2*y);
29111
29112 pY[c] = (ma_int16)ma_clamp(y, -32768, 32767);
29113 pBQ->r1[c].s32 = r1;
29114 pBQ->r2[c].s32 = r2;
29115 }
29116}
29117
29118static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
29119{
29120 ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
29121}
29122
29123ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
29124{
29125 ma_uint32 n;
29126
29127 if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) {
29128 return MA_INVALID_ARGS;
29129 }
29130
29131 /* 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. */
29132
29133 if (pBQ->format == ma_format_f32) {
29134 /* */ float* pY = ( float*)pFramesOut;
29135 const float* pX = (const float*)pFramesIn;
29136
29137 for (n = 0; n < frameCount; n += 1) {
29138 ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
29139 pY += pBQ->channels;
29140 pX += pBQ->channels;
29141 }
29142 } else if (pBQ->format == ma_format_s16) {
29143 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
29144 const ma_int16* pX = (const ma_int16*)pFramesIn;
29145
29146 for (n = 0; n < frameCount; n += 1) {
29147 ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
29148 pY += pBQ->channels;
29149 pX += pBQ->channels;
29150 }
29151 } else {
29152 MA_ASSERT(MA_FALSE);
29153 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
29154 }
29155
29156 return MA_SUCCESS;
29157}
29158
29159ma_uint32 ma_biquad_get_latency(ma_biquad* pBQ)
29160{
29161 if (pBQ == NULL) {
29162 return 0;
29163 }
29164
29165 return 2;
29166}
29167
29168
29169/**************************************************************************************************************************************************************
29170
29171Low-Pass Filter
29172
29173**************************************************************************************************************************************************************/
29174ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
29175{
29176 ma_lpf_config config;
29177
29178 MA_ZERO_OBJECT(&config);
29179 config.format = format;
29180 config.channels = channels;
29181 config.sampleRate = sampleRate;
29182 config.cutoffFrequency = cutoffFrequency;
29183
29184 return config;
29185}
29186
29187static MA_INLINE ma_biquad_config ma_lpf__get_biquad_config(const ma_lpf_config* pConfig)
29188{
29189 ma_biquad_config bqConfig;
29190 double q;
29191 double w;
29192 double s;
29193 double c;
29194 double a;
29195
29196 MA_ASSERT(pConfig != NULL);
29197
29198 q = 0.707107;
29199 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
29200 s = ma_sin(w);
29201 c = ma_cos(w);
29202 a = s / (2*q);
29203
29204 bqConfig.b0 = (1 - c) / 2;
29205 bqConfig.b1 = 1 - c;
29206 bqConfig.b2 = (1 - c) / 2;
29207 bqConfig.a0 = 1 + a;
29208 bqConfig.a1 = -2 * c;
29209 bqConfig.a2 = 1 - a;
29210
29211 bqConfig.format = pConfig->format;
29212 bqConfig.channels = pConfig->channels;
29213
29214 return bqConfig;
29215}
29216
29217ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF)
29218{
29219 ma_result result;
29220 ma_biquad_config bqConfig;
29221
29222 if (pLPF == NULL) {
29223 return MA_INVALID_ARGS;
29224 }
29225
29226 MA_ZERO_OBJECT(pLPF);
29227
29228 if (pConfig == NULL) {
29229 return MA_INVALID_ARGS;
29230 }
29231
29232 bqConfig = ma_lpf__get_biquad_config(pConfig);
29233 result = ma_biquad_init(&bqConfig, &pLPF->bq);
29234 if (result != MA_SUCCESS) {
29235 return result;
29236 }
29237
29238 return MA_SUCCESS;
29239}
29240
29241ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF)
29242{
29243 ma_result result;
29244 ma_biquad_config bqConfig;
29245
29246 if (pLPF == NULL || pConfig == NULL) {
29247 return MA_INVALID_ARGS;
29248 }
29249
29250 bqConfig = ma_lpf__get_biquad_config(pConfig);
29251 result = ma_biquad_reinit(&bqConfig, &pLPF->bq);
29252 if (result != MA_SUCCESS) {
29253 return result;
29254 }
29255
29256 return MA_SUCCESS;
29257}
29258
29259static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
29260{
29261 ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn);
29262}
29263
29264static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pFrameOut, const float* pFrameIn)
29265{
29266 ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn);
29267}
29268
29269ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
29270{
29271 if (pLPF == NULL) {
29272 return MA_INVALID_ARGS;
29273 }
29274
29275 return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount);
29276}
29277
29278ma_uint32 ma_lpf_get_latency(ma_lpf* pLPF)
29279{
29280 if (pLPF == NULL) {
29281 return 0;
29282 }
29283
29284 return ma_biquad_get_latency(&pLPF->bq);
29285}
29286
29287
29288/**************************************************************************************************************************************************************
29289
29290High-Pass Filtering
29291
29292**************************************************************************************************************************************************************/
29293ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
29294{
29295 ma_hpf_config config;
29296
29297 MA_ZERO_OBJECT(&config);
29298 config.format = format;
29299 config.channels = channels;
29300 config.sampleRate = sampleRate;
29301 config.cutoffFrequency = cutoffFrequency;
29302
29303 return config;
29304}
29305
29306static MA_INLINE ma_biquad_config ma_hpf__get_biquad_config(const ma_hpf_config* pConfig)
29307{
29308 ma_biquad_config bqConfig;
29309 double q;
29310 double w;
29311 double s;
29312 double c;
29313 double a;
29314
29315 MA_ASSERT(pConfig != NULL);
29316
29317 q = 0.707107;
29318 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
29319 s = ma_sin(w);
29320 c = ma_cos(w);
29321 a = s / (2*q);
29322
29323 bqConfig.b0 = (1 + c) / 2;
29324 bqConfig.b1 = -(1 + c);
29325 bqConfig.b2 = (1 + c) / 2;
29326 bqConfig.a0 = 1 + a;
29327 bqConfig.a1 = -2 * c;
29328 bqConfig.a2 = 1 - a;
29329
29330 bqConfig.format = pConfig->format;
29331 bqConfig.channels = pConfig->channels;
29332
29333 return bqConfig;
29334}
29335
29336ma_result ma_hpf_init(const ma_hpf_config* pConfig, ma_hpf* pHPF)
29337{
29338 ma_result result;
29339 ma_biquad_config bqConfig;
29340
29341 if (pHPF == NULL) {
29342 return MA_INVALID_ARGS;
29343 }
29344
29345 MA_ZERO_OBJECT(pHPF);
29346
29347 if (pConfig == NULL) {
29348 return MA_INVALID_ARGS;
29349 }
29350
29351 bqConfig = ma_hpf__get_biquad_config(pConfig);
29352 result = ma_biquad_init(&bqConfig, &pHPF->bq);
29353 if (result != MA_SUCCESS) {
29354 return result;
29355 }
29356
29357 return MA_SUCCESS;
29358}
29359
29360ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF)
29361{
29362 ma_result result;
29363 ma_biquad_config bqConfig;
29364
29365 if (pHPF == NULL || pConfig == NULL) {
29366 return MA_INVALID_ARGS;
29367 }
29368
29369 bqConfig = ma_hpf__get_biquad_config(pConfig);
29370 result = ma_biquad_reinit(&bqConfig, &pHPF->bq);
29371 if (result != MA_SUCCESS) {
29372 return result;
29373 }
29374
29375 return MA_SUCCESS;
29376}
29377
29378ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
29379{
29380 if (pHPF == NULL) {
29381 return MA_INVALID_ARGS;
29382 }
29383
29384 return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount);
29385}
29386
29387ma_uint32 ma_hpf_get_latency(ma_hpf* pHPF)
29388{
29389 if (pHPF == NULL) {
29390 return 0;
29391 }
29392
29393 return ma_biquad_get_latency(&pHPF->bq);
29394}
29395
29396
29397/**************************************************************************************************************************************************************
29398
29399Band-Pass Filtering
29400
29401**************************************************************************************************************************************************************/
29402ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
29403{
29404 ma_bpf_config config;
29405
29406 MA_ZERO_OBJECT(&config);
29407 config.format = format;
29408 config.channels = channels;
29409 config.sampleRate = sampleRate;
29410 config.cutoffFrequency = cutoffFrequency;
29411
29412 return config;
29413}
29414
29415
29416static MA_INLINE ma_biquad_config ma_bpf__get_biquad_config(const ma_bpf_config* pConfig)
29417{
29418 ma_biquad_config bqConfig;
29419 double q;
29420 double w;
29421 double s;
29422 double c;
29423 double a;
29424
29425 MA_ASSERT(pConfig != NULL);
29426
29427 q = 0.707107;
29428 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
29429 s = ma_sin(w);
29430 c = ma_cos(w);
29431 a = s / (2*q);
29432
29433 bqConfig.b0 = q * a;
29434 bqConfig.b1 = 0;
29435 bqConfig.b2 = -q * a;
29436 bqConfig.a0 = 1 + a;
29437 bqConfig.a1 = -2 * c;
29438 bqConfig.a2 = 1 - a;
29439
29440 bqConfig.format = pConfig->format;
29441 bqConfig.channels = pConfig->channels;
29442
29443 return bqConfig;
29444}
29445
29446ma_result ma_bpf_init(const ma_bpf_config* pConfig, ma_bpf* pBPF)
29447{
29448 ma_result result;
29449 ma_biquad_config bqConfig;
29450
29451 if (pBPF == NULL) {
29452 return MA_INVALID_ARGS;
29453 }
29454
29455 MA_ZERO_OBJECT(pBPF);
29456
29457 if (pConfig == NULL) {
29458 return MA_INVALID_ARGS;
29459 }
29460
29461 bqConfig = ma_bpf__get_biquad_config(pConfig);
29462 result = ma_biquad_init(&bqConfig, &pBPF->bq);
29463 if (result != MA_SUCCESS) {
29464 return result;
29465 }
29466
29467 return MA_SUCCESS;
29468}
29469
29470ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF)
29471{
29472 ma_result result;
29473 ma_biquad_config bqConfig;
29474
29475 if (pBPF == NULL || pConfig == NULL) {
29476 return MA_INVALID_ARGS;
29477 }
29478
29479 bqConfig = ma_bpf__get_biquad_config(pConfig);
29480 result = ma_biquad_reinit(&bqConfig, &pBPF->bq);
29481 if (result != MA_SUCCESS) {
29482 return result;
29483 }
29484
29485 return MA_SUCCESS;
29486}
29487
29488ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
29489{
29490 if (pBPF == NULL) {
29491 return MA_INVALID_ARGS;
29492 }
29493
29494 return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount);
29495}
29496
29497ma_uint32 ma_bpf_get_latency(ma_bpf* pBPF)
29498{
29499 if (pBPF == NULL) {
29500 return 0;
29501 }
29502
29503 return ma_biquad_get_latency(&pBPF->bq);
29504}
29505
29506
29507
29508/**************************************************************************************************************************************************************
29509
29510Resampling
29511
29512**************************************************************************************************************************************************************/
29513ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
29514{
29515 ma_linear_resampler_config config;
29516 MA_ZERO_OBJECT(&config);
29517 config.format = format;
29518 config.channels = channels;
29519 config.sampleRateIn = sampleRateIn;
29520 config.sampleRateOut = sampleRateOut;
29521 config.lpfCount = 1;
29522 config.lpfNyquistFactor = 1;
29523
29524 return config;
29525}
29526
29527static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized)
29528{
29529 ma_uint32 gcf;
29530
29531 if (pResampler == NULL) {
29532 return MA_INVALID_ARGS;
29533 }
29534
29535 if (sampleRateIn == 0 || sampleRateOut == 0) {
29536 return MA_INVALID_ARGS;
29537 }
29538
29539 /* Simplify the sample rate. */
29540 gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut);
29541 pResampler->config.sampleRateIn /= gcf;
29542 pResampler->config.sampleRateOut /= gcf;
29543
29544 if (pResampler->config.lpfCount > 0) {
29545 ma_result result;
29546 ma_uint32 iFilter;
29547 ma_uint32 lpfSampleRate;
29548 double lpfCutoffFrequency;
29549 ma_lpf_config lpfConfig;
29550
29551 if (pResampler->config.lpfCount > MA_MAX_RESAMPLER_LPF_FILTERS) {
29552 return MA_INVALID_ARGS;
29553 }
29554
29555 lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut));
29556 lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor);
29557
29558 lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency);
29559
29560 /*
29561 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
29562 getting cleared. Instead we re-initialize the filter which will maintain any cached frames.
29563 */
29564 result = MA_SUCCESS;
29565 for (iFilter = 0; iFilter < pResampler->config.lpfCount; iFilter += 1) {
29566 if (isResamplerAlreadyInitialized) {
29567 result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf[iFilter]);
29568 } else {
29569 result = ma_lpf_init(&lpfConfig, &pResampler->lpf[iFilter]);
29570 }
29571
29572 if (result != MA_SUCCESS) {
29573 break;
29574 }
29575 }
29576
29577 if (result != MA_SUCCESS) {
29578 return result; /* Failed to initialize the low-pass filter. */
29579 }
29580 }
29581
29582 pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut;
29583 pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut;
29584
29585 /* Make sure the fractional part is less than the output sample rate. */
29586 pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut;
29587 pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut;
29588
29589 return MA_SUCCESS;
29590}
29591
29592ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, ma_linear_resampler* pResampler)
29593{
29594 ma_result result;
29595
29596 if (pResampler == NULL) {
29597 return MA_INVALID_ARGS;
29598 }
29599
29600 MA_ZERO_OBJECT(pResampler);
29601
29602 if (pConfig == NULL) {
29603 return MA_INVALID_ARGS;
29604 }
29605
29606 pResampler->config = *pConfig;
29607
29608 /* Setting the rate will set up the filter and time advances for us. */
29609 result = ma_linear_resampler_set_rate_internal(pResampler, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE);
29610 if (result != MA_SUCCESS) {
29611 return result;
29612 }
29613
29614 pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */
29615 pResampler->inTimeFrac = 0;
29616
29617 return MA_SUCCESS;
29618}
29619
29620void ma_linear_resampler_uninit(ma_linear_resampler* pResampler)
29621{
29622 if (pResampler == NULL) {
29623 return;
29624 }
29625}
29626
29627static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift)
29628{
29629 ma_int32 b;
29630 ma_int32 c;
29631 ma_int32 r;
29632
29633 MA_ASSERT(a <= (1<<shift));
29634
29635 b = x * ((1<<shift) - a);
29636 c = y * a;
29637 r = b + c;
29638
29639 return (ma_int16)(r >> shift);
29640}
29641
29642static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* pFrameOut)
29643{
29644 ma_uint32 c;
29645 ma_uint32 a;
29646 const ma_uint32 shift = 12;
29647
29648 MA_ASSERT(pResampler != NULL);
29649 MA_ASSERT(pFrameOut != NULL);
29650
29651 a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut;
29652
29653 for (c = 0; c < pResampler->config.channels; c += 1) {
29654 ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift);
29655 pFrameOut[c] = s;
29656 }
29657}
29658
29659
29660static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* pFrameOut)
29661{
29662 ma_uint32 c;
29663 float a;
29664
29665 MA_ASSERT(pResampler != NULL);
29666 MA_ASSERT(pFrameOut != NULL);
29667
29668 a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut;
29669
29670 for (c = 0; c < pResampler->config.channels; c += 1) {
29671 float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a);
29672 pFrameOut[c] = s;
29673 }
29674}
29675
29676static 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)
29677{
29678 const ma_int16* pFramesInS16;
29679 /* */ ma_int16* pFramesOutS16;
29680 ma_uint64 frameCountIn;
29681 ma_uint64 frameCountOut;
29682 ma_uint64 framesProcessedIn;
29683 ma_uint64 framesProcessedOut;
29684
29685 MA_ASSERT(pResampler != NULL);
29686 MA_ASSERT(pFrameCountIn != NULL);
29687 MA_ASSERT(pFrameCountOut != NULL);
29688
29689 pFramesInS16 = (const ma_int16*)pFramesIn;
29690 pFramesOutS16 = ( ma_int16*)pFramesOut;
29691 frameCountIn = *pFrameCountIn;
29692 frameCountOut = *pFrameCountOut;
29693 framesProcessedIn = 0;
29694 framesProcessedOut = 0;
29695
29696 for (;;) {
29697 if (framesProcessedOut >= frameCountOut) {
29698 break;
29699 }
29700
29701 /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */
29702 while (pResampler->inTimeInt > 0 && frameCountIn > 0) {
29703 ma_uint32 iFilter;
29704 ma_uint32 iChannel;
29705
29706 if (pFramesInS16 != NULL) {
29707 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
29708 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
29709 pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
29710 }
29711 pFramesInS16 += pResampler->config.channels;
29712 } else {
29713 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
29714 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
29715 pResampler->x1.s16[iChannel] = 0;
29716 }
29717 }
29718
29719 /* Filter. */
29720 for (iFilter = 0; iFilter < pResampler->config.lpfCount; iFilter += 1) {
29721 ma_lpf_process_pcm_frame_s16(&pResampler->lpf[iFilter], pResampler->x1.s16, pResampler->x1.s16);
29722 }
29723
29724 frameCountIn -= 1;
29725 framesProcessedIn += 1;
29726 pResampler->inTimeInt -= 1;
29727 }
29728
29729 if (pResampler->inTimeInt > 0) {
29730 break; /* Ran out of input data. */
29731 }
29732
29733 /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */
29734 if (pFramesOutS16 != NULL) {
29735 MA_ASSERT(pResampler->inTimeInt == 0);
29736 ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
29737
29738 pFramesOutS16 += pResampler->config.channels;
29739 }
29740
29741 framesProcessedOut += 1;
29742
29743 /* Advance time forward. */
29744 pResampler->inTimeInt += pResampler->inAdvanceInt;
29745 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
29746 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
29747 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
29748 pResampler->inTimeInt += 1;
29749 }
29750 }
29751
29752 *pFrameCountIn = framesProcessedIn;
29753 *pFrameCountOut = framesProcessedOut;
29754
29755 return MA_SUCCESS;
29756}
29757
29758static 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)
29759{
29760 const ma_int16* pFramesInS16;
29761 /* */ ma_int16* pFramesOutS16;
29762 ma_uint64 frameCountIn;
29763 ma_uint64 frameCountOut;
29764 ma_uint64 framesProcessedIn;
29765 ma_uint64 framesProcessedOut;
29766
29767 MA_ASSERT(pResampler != NULL);
29768 MA_ASSERT(pFrameCountIn != NULL);
29769 MA_ASSERT(pFrameCountOut != NULL);
29770
29771 pFramesInS16 = (const ma_int16*)pFramesIn;
29772 pFramesOutS16 = ( ma_int16*)pFramesOut;
29773 frameCountIn = *pFrameCountIn;
29774 frameCountOut = *pFrameCountOut;
29775 framesProcessedIn = 0;
29776 framesProcessedOut = 0;
29777
29778 for (;;) {
29779 ma_uint32 iFilter;
29780
29781 if (framesProcessedOut >= frameCountOut) {
29782 break;
29783 }
29784
29785 /* Before interpolating we need to load the buffers. */
29786 while (pResampler->inTimeInt > 0 && frameCountIn > 0) {
29787 ma_uint32 iChannel;
29788
29789 if (pFramesInS16 != NULL) {
29790 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
29791 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
29792 pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
29793 }
29794 pFramesInS16 += pResampler->config.channels;
29795 } else {
29796 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
29797 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
29798 pResampler->x1.s16[iChannel] = 0;
29799 }
29800 }
29801
29802 frameCountIn -= 1;
29803 framesProcessedIn += 1;
29804 pResampler->inTimeInt -= 1;
29805 }
29806
29807 if (pResampler->inTimeInt > 0) {
29808 break; /* Ran out of input data. */
29809 }
29810
29811 /* Getting here means the frames have been loaded and we can generate the next output frame. */
29812 if (pFramesOutS16 != NULL) {
29813 MA_ASSERT(pResampler->inTimeInt == 0);
29814 ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
29815
29816 /* Filter. */
29817 for (iFilter = 0; iFilter < pResampler->config.lpfCount; iFilter += 1) {
29818 ma_lpf_process_pcm_frame_s16(&pResampler->lpf[iFilter], pFramesOutS16, pFramesOutS16);
29819 }
29820
29821 pFramesOutS16 += pResampler->config.channels;
29822 }
29823
29824 framesProcessedOut += 1;
29825
29826 /* Advance time forward. */
29827 pResampler->inTimeInt += pResampler->inAdvanceInt;
29828 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
29829 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
29830 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
29831 pResampler->inTimeInt += 1;
29832 }
29833 }
29834
29835 *pFrameCountIn = framesProcessedIn;
29836 *pFrameCountOut = framesProcessedOut;
29837
29838 return MA_SUCCESS;
29839}
29840
29841static ma_result ma_linear_resampler_process_pcm_frames_s16(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
29842{
29843 MA_ASSERT(pResampler != NULL);
29844
29845 if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
29846 return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
29847 } else {
29848 return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
29849 }
29850}
29851
29852
29853static 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)
29854{
29855 const float* pFramesInF32;
29856 /* */ float* pFramesOutF32;
29857 ma_uint64 frameCountIn;
29858 ma_uint64 frameCountOut;
29859 ma_uint64 framesProcessedIn;
29860 ma_uint64 framesProcessedOut;
29861
29862 MA_ASSERT(pResampler != NULL);
29863 MA_ASSERT(pFrameCountIn != NULL);
29864 MA_ASSERT(pFrameCountOut != NULL);
29865
29866 pFramesInF32 = (const float*)pFramesIn;
29867 pFramesOutF32 = ( float*)pFramesOut;
29868 frameCountIn = *pFrameCountIn;
29869 frameCountOut = *pFrameCountOut;
29870 framesProcessedIn = 0;
29871 framesProcessedOut = 0;
29872
29873 for (;;) {
29874 if (framesProcessedOut >= frameCountOut) {
29875 break;
29876 }
29877
29878 /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */
29879 while (pResampler->inTimeInt > 0 && frameCountIn > 0) {
29880 ma_uint32 iFilter;
29881 ma_uint32 iChannel;
29882
29883 if (pFramesInF32 != NULL) {
29884 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
29885 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
29886 pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
29887 }
29888 pFramesInF32 += pResampler->config.channels;
29889 } else {
29890 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
29891 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
29892 pResampler->x1.f32[iChannel] = 0;
29893 }
29894 }
29895
29896 /* Filter. */
29897 for (iFilter = 0; iFilter < pResampler->config.lpfCount; iFilter += 1) {
29898 ma_lpf_process_pcm_frame_f32(&pResampler->lpf[iFilter], pResampler->x1.f32, pResampler->x1.f32);
29899 }
29900
29901 frameCountIn -= 1;
29902 framesProcessedIn += 1;
29903 pResampler->inTimeInt -= 1;
29904 }
29905
29906 if (pResampler->inTimeInt > 0) {
29907 break; /* Ran out of input data. */
29908 }
29909
29910 /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */
29911 if (pFramesOutF32 != NULL) {
29912 MA_ASSERT(pResampler->inTimeInt == 0);
29913 ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
29914
29915 pFramesOutF32 += pResampler->config.channels;
29916 }
29917
29918 framesProcessedOut += 1;
29919
29920 /* Advance time forward. */
29921 pResampler->inTimeInt += pResampler->inAdvanceInt;
29922 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
29923 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
29924 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
29925 pResampler->inTimeInt += 1;
29926 }
29927 }
29928
29929 *pFrameCountIn = framesProcessedIn;
29930 *pFrameCountOut = framesProcessedOut;
29931
29932 return MA_SUCCESS;
29933}
29934
29935static 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)
29936{
29937 const float* pFramesInF32;
29938 /* */ float* pFramesOutF32;
29939 ma_uint64 frameCountIn;
29940 ma_uint64 frameCountOut;
29941 ma_uint64 framesProcessedIn;
29942 ma_uint64 framesProcessedOut;
29943
29944 MA_ASSERT(pResampler != NULL);
29945 MA_ASSERT(pFrameCountIn != NULL);
29946 MA_ASSERT(pFrameCountOut != NULL);
29947
29948 pFramesInF32 = (const float*)pFramesIn;
29949 pFramesOutF32 = ( float*)pFramesOut;
29950 frameCountIn = *pFrameCountIn;
29951 frameCountOut = *pFrameCountOut;
29952 framesProcessedIn = 0;
29953 framesProcessedOut = 0;
29954
29955 for (;;) {
29956 ma_uint32 iFilter;
29957
29958 if (framesProcessedOut >= frameCountOut) {
29959 break;
29960 }
29961
29962 /* Before interpolating we need to load the buffers. */
29963 while (pResampler->inTimeInt > 0 && frameCountIn > 0) {
29964 ma_uint32 iChannel;
29965
29966 if (pFramesInF32 != NULL) {
29967 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
29968 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
29969 pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
29970 }
29971 pFramesInF32 += pResampler->config.channels;
29972 } else {
29973 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
29974 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
29975 pResampler->x1.f32[iChannel] = 0;
29976 }
29977 }
29978
29979 frameCountIn -= 1;
29980 framesProcessedIn += 1;
29981 pResampler->inTimeInt -= 1;
29982 }
29983
29984 if (pResampler->inTimeInt > 0) {
29985 break; /* Ran out of input data. */
29986 }
29987
29988 /* Getting here means the frames have been loaded and we can generate the next output frame. */
29989 if (pFramesOutF32 != NULL) {
29990 MA_ASSERT(pResampler->inTimeInt == 0);
29991 ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
29992
29993 /* Filter. */
29994 for (iFilter = 0; iFilter < pResampler->config.lpfCount; iFilter += 1) {
29995 ma_lpf_process_pcm_frame_f32(&pResampler->lpf[iFilter], pFramesOutF32, pFramesOutF32);
29996 }
29997
29998 pFramesOutF32 += pResampler->config.channels;
29999 }
30000
30001 framesProcessedOut += 1;
30002
30003 /* Advance time forward. */
30004 pResampler->inTimeInt += pResampler->inAdvanceInt;
30005 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
30006 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
30007 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
30008 pResampler->inTimeInt += 1;
30009 }
30010 }
30011
30012 *pFrameCountIn = framesProcessedIn;
30013 *pFrameCountOut = framesProcessedOut;
30014
30015 return MA_SUCCESS;
30016}
30017
30018static ma_result ma_linear_resampler_process_pcm_frames_f32(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
30019{
30020 MA_ASSERT(pResampler != NULL);
30021
30022 if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
30023 return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
30024 } else {
30025 return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
30026 }
30027}
30028
30029
30030ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
30031{
30032 if (pResampler == NULL) {
30033 return MA_INVALID_ARGS;
30034 }
30035
30036 /* */ if (pResampler->config.format == ma_format_s16) {
30037 return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
30038 } else if (pResampler->config.format == ma_format_f32) {
30039 return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
30040 } else {
30041 /* 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(). */
30042 MA_ASSERT(MA_FALSE);
30043 return MA_INVALID_ARGS;
30044 }
30045}
30046
30047
30048ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
30049{
30050 return ma_linear_resampler_set_rate_internal(pResampler, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE);
30051}
30052
30053ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut)
30054{
30055 ma_uint32 n;
30056 ma_uint32 d;
30057
30058 d = 1000000; /* We use up to 6 decimal places. */
30059 n = (ma_uint32)(ratioInOut * d);
30060
30061 if (n == 0) {
30062 return MA_INVALID_ARGS; /* Ratio too small. */
30063 }
30064
30065 MA_ASSERT(n != 0);
30066
30067 return ma_linear_resampler_set_rate(pResampler, n, d);
30068}
30069
30070
30071ma_uint64 ma_linear_resampler_get_required_input_frame_count(ma_linear_resampler* pResampler, ma_uint64 outputFrameCount)
30072{
30073 ma_uint64 count;
30074
30075 if (pResampler == NULL) {
30076 return 0;
30077 }
30078
30079 if (outputFrameCount == 0) {
30080 return 0;
30081 }
30082
30083 /* Any whole input frames are consumed before the first output frame is generated. */
30084 count = pResampler->inTimeInt;
30085 outputFrameCount -= 1;
30086
30087 /* The rest of the output frames can be calculated in constant time. */
30088 count += outputFrameCount * pResampler->inAdvanceInt;
30089 count += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut;
30090
30091 return count;
30092}
30093
30094ma_uint64 ma_linear_resampler_get_expected_output_frame_count(ma_linear_resampler* pResampler, ma_uint64 inputFrameCount)
30095{
30096 ma_uint64 outputFrameCount;
30097 ma_uint64 inTimeInt;
30098 ma_uint64 inTimeFrac;
30099
30100 if (pResampler == NULL) {
30101 return 0;
30102 }
30103
30104 /* TODO: Try making this run in constant time. */
30105
30106 outputFrameCount = 0;
30107 inTimeInt = pResampler->inTimeInt;
30108 inTimeFrac = pResampler->inTimeFrac;
30109
30110 for (;;) {
30111 while (inTimeInt > 0 && inputFrameCount > 0) {
30112 inputFrameCount -= 1;
30113 inTimeInt -= 1;
30114 }
30115
30116 if (inTimeInt > 0) {
30117 break;
30118 }
30119
30120 outputFrameCount += 1;
30121
30122 /* Advance time forward. */
30123 inTimeInt += pResampler->inAdvanceInt;
30124 inTimeFrac += pResampler->inAdvanceFrac;
30125 if (inTimeFrac >= pResampler->config.sampleRateOut) {
30126 inTimeFrac -= pResampler->config.sampleRateOut;
30127 inTimeInt += 1;
30128 }
30129 }
30130
30131 return outputFrameCount;
30132}
30133
30134ma_uint64 ma_linear_resampler_get_input_latency(ma_linear_resampler* pResampler)
30135{
30136 ma_uint32 latency;
30137 ma_uint32 iFilter;
30138
30139 if (pResampler == NULL) {
30140 return 0;
30141 }
30142
30143 latency = 1;
30144 for (iFilter = 0; iFilter < pResampler->config.lpfCount; iFilter += 1) {
30145 latency += ma_lpf_get_latency(&pResampler->lpf[iFilter]);
30146 }
30147
30148 return latency;
30149}
30150
30151ma_uint64 ma_linear_resampler_get_output_latency(ma_linear_resampler* pResampler)
30152{
30153 if (pResampler == NULL) {
30154 return 0;
30155 }
30156
30157 return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn;
30158}
30159
30160
30161#if defined(ma_speex_resampler_h)
30162#define MA_HAS_SPEEX_RESAMPLER
30163
30164static ma_result ma_result_from_speex_err(int err)
30165{
30166 switch (err)
30167 {
30168 case RESAMPLER_ERR_SUCCESS: return MA_SUCCESS;
30169 case RESAMPLER_ERR_ALLOC_FAILED: return MA_OUT_OF_MEMORY;
30170 case RESAMPLER_ERR_BAD_STATE: return MA_ERROR;
30171 case RESAMPLER_ERR_INVALID_ARG: return MA_INVALID_ARGS;
30172 case RESAMPLER_ERR_PTR_OVERLAP: return MA_INVALID_ARGS;
30173 case RESAMPLER_ERR_OVERFLOW: return MA_ERROR;
30174 default: return MA_ERROR;
30175 }
30176}
30177#endif /* ma_speex_resampler_h */
30178
30179ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm)
30180{
30181 ma_resampler_config config;
30182
30183 MA_ZERO_OBJECT(&config);
30184 config.format = format;
30185 config.channels = channels;
30186 config.sampleRateIn = sampleRateIn;
30187 config.sampleRateOut = sampleRateOut;
30188 config.algorithm = algorithm;
30189
30190 /* Linear. */
30191 config.linear.lpfCount = 1;
30192 config.linear.lpfNyquistFactor = 1;
30193
30194 /* Speex. */
30195 config.speex.quality = 3; /* Cannot leave this as 0 as that is actually a valid value for Speex resampling quality. */
30196
30197 return config;
30198}
30199
30200ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pResampler)
30201{
30202 ma_result result;
30203
30204 if (pResampler == NULL) {
30205 return MA_INVALID_ARGS;
30206 }
30207
30208 MA_ZERO_OBJECT(pResampler);
30209
30210 if (pConfig == NULL) {
30211 return MA_INVALID_ARGS;
30212 }
30213
30214 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
30215 return MA_INVALID_ARGS;
30216 }
30217
30218 pResampler->config = *pConfig;
30219
30220 switch (pConfig->algorithm)
30221 {
30222 case ma_resample_algorithm_linear:
30223 {
30224 ma_linear_resampler_config linearConfig;
30225 linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut);
30226 linearConfig.lpfCount = pConfig->linear.lpfCount;
30227 linearConfig.lpfNyquistFactor = pConfig->linear.lpfNyquistFactor;
30228
30229 result = ma_linear_resampler_init(&linearConfig, &pResampler->state.linear);
30230 if (result != MA_SUCCESS) {
30231 return result;
30232 }
30233 } break;
30234
30235 case ma_resample_algorithm_speex:
30236 {
30237 #if defined(MA_HAS_SPEEX_RESAMPLER)
30238 int speexErr;
30239 pResampler->state.speex.pSpeexResamplerState = speex_resampler_init(pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->speex.quality, &speexErr);
30240 if (pResampler->state.speex.pSpeexResamplerState == NULL) {
30241 return ma_result_from_speex_err(speexErr);
30242 }
30243 #else
30244 /* Speex resampler not available. */
30245 return MA_NO_BACKEND;
30246 #endif
30247 } break;
30248
30249 default: return MA_INVALID_ARGS;
30250 }
30251
30252 return MA_SUCCESS;
30253}
30254
30255void ma_resampler_uninit(ma_resampler* pResampler)
30256{
30257 if (pResampler == NULL) {
30258 return;
30259 }
30260
30261 if (pResampler->config.algorithm == ma_resample_algorithm_linear) {
30262 ma_linear_resampler_uninit(&pResampler->state.linear);
30263 }
30264
30265#if defined(MA_HAS_SPEEX_RESAMPLER)
30266 if (pResampler->config.algorithm == ma_resample_algorithm_speex) {
30267 speex_resampler_destroy((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState);
30268 }
30269#endif
30270}
30271
30272static ma_result ma_resampler_process_pcm_frames__read__linear(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
30273{
30274 return ma_linear_resampler_process_pcm_frames(&pResampler->state.linear, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
30275}
30276
30277#if defined(MA_HAS_SPEEX_RESAMPLER)
30278static ma_result ma_resampler_process_pcm_frames__read__speex(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
30279{
30280 int speexErr;
30281 ma_uint64 frameCountOut;
30282 ma_uint64 frameCountIn;
30283 ma_uint64 framesProcessedOut;
30284 ma_uint64 framesProcessedIn;
30285 unsigned int framesPerIteration = UINT_MAX;
30286
30287 MA_ASSERT(pResampler != NULL);
30288 MA_ASSERT(pFramesOut != NULL);
30289 MA_ASSERT(pFrameCountOut != NULL);
30290 MA_ASSERT(pFrameCountIn != NULL);
30291
30292 /*
30293 Reading from the Speex resampler requires a bit of dancing around for a few reasons. The first thing is that it's frame counts
30294 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
30295 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
30296 do this is for ma_resampler_get_required_input_frame_count() and ma_resampler_get_expected_output_frame_count().
30297 */
30298 frameCountOut = *pFrameCountOut;
30299 frameCountIn = *pFrameCountIn;
30300 framesProcessedOut = 0;
30301 framesProcessedIn = 0;
30302
30303 while (framesProcessedOut < frameCountOut && framesProcessedIn < frameCountIn) {
30304 unsigned int frameCountInThisIteration;
30305 unsigned int frameCountOutThisIteration;
30306 const void* pFramesInThisIteration;
30307 void* pFramesOutThisIteration;
30308
30309 frameCountInThisIteration = framesPerIteration;
30310 if ((ma_uint64)frameCountInThisIteration > (frameCountIn - framesProcessedIn)) {
30311 frameCountInThisIteration = (unsigned int)(frameCountIn - framesProcessedIn);
30312 }
30313
30314 frameCountOutThisIteration = framesPerIteration;
30315 if ((ma_uint64)frameCountOutThisIteration > (frameCountOut - framesProcessedOut)) {
30316 frameCountOutThisIteration = (unsigned int)(frameCountOut - framesProcessedOut);
30317 }
30318
30319 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels));
30320 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels));
30321
30322 if (pResampler->config.format == ma_format_f32) {
30323 speexErr = speex_resampler_process_interleaved_float((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, (const float*)pFramesInThisIteration, &frameCountInThisIteration, (float*)pFramesOutThisIteration, &frameCountOutThisIteration);
30324 } else if (pResampler->config.format == ma_format_s16) {
30325 speexErr = speex_resampler_process_interleaved_int((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, (const spx_int16_t*)pFramesInThisIteration, &frameCountInThisIteration, (spx_int16_t*)pFramesOutThisIteration, &frameCountOutThisIteration);
30326 } else {
30327 /* Format not supported. Should never get here. */
30328 MA_ASSERT(MA_FALSE);
30329 return MA_INVALID_OPERATION;
30330 }
30331
30332 if (speexErr != RESAMPLER_ERR_SUCCESS) {
30333 return ma_result_from_speex_err(speexErr);
30334 }
30335
30336 framesProcessedIn += frameCountInThisIteration;
30337 framesProcessedOut += frameCountOutThisIteration;
30338 }
30339
30340 *pFrameCountOut = framesProcessedOut;
30341 *pFrameCountIn = framesProcessedIn;
30342
30343 return MA_SUCCESS;
30344}
30345#endif
30346
30347static ma_result ma_resampler_process_pcm_frames__read(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
30348{
30349 MA_ASSERT(pResampler != NULL);
30350 MA_ASSERT(pFramesOut != NULL);
30351
30352 /* pFramesOut is not NULL, which means we must have a capacity. */
30353 if (pFrameCountOut == NULL) {
30354 return MA_INVALID_ARGS;
30355 }
30356
30357 /* It doesn't make sense to not have any input frames to process. */
30358 if (pFrameCountIn == NULL || pFramesIn == NULL) {
30359 return MA_INVALID_ARGS;
30360 }
30361
30362 switch (pResampler->config.algorithm)
30363 {
30364 case ma_resample_algorithm_linear:
30365 {
30366 return ma_resampler_process_pcm_frames__read__linear(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
30367 }
30368
30369 case ma_resample_algorithm_speex:
30370 {
30371 #if defined(MA_HAS_SPEEX_RESAMPLER)
30372 return ma_resampler_process_pcm_frames__read__speex(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
30373 #else
30374 break;
30375 #endif
30376 }
30377
30378 default: break;
30379 }
30380
30381 /* Should never get here. */
30382 MA_ASSERT(MA_FALSE);
30383 return MA_INVALID_ARGS;
30384}
30385
30386
30387static ma_result ma_resampler_process_pcm_frames__seek__linear(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut)
30388{
30389 MA_ASSERT(pResampler != NULL);
30390
30391 /* Seeking is supported natively by the linear resampler. */
30392 return ma_linear_resampler_process_pcm_frames(&pResampler->state.linear, pFramesIn, pFrameCountIn, NULL, pFrameCountOut);
30393}
30394
30395#if defined(MA_HAS_SPEEX_RESAMPLER)
30396static ma_result ma_resampler_process_pcm_frames__seek__speex(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut)
30397{
30398 /* The generic seek method is implemented in on top of ma_resampler_process_pcm_frames__read() by just processing into a dummy buffer. */
30399 float devnull[8192];
30400 ma_uint64 totalOutputFramesToProcess;
30401 ma_uint64 totalOutputFramesProcessed;
30402 ma_uint64 totalInputFramesProcessed;
30403 ma_uint32 bpf;
30404 ma_result result;
30405
30406 MA_ASSERT(pResampler != NULL);
30407
30408 totalOutputFramesProcessed = 0;
30409 totalInputFramesProcessed = 0;
30410 bpf = ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels);
30411
30412 if (pFrameCountOut != NULL) {
30413 /* Seek by output frames. */
30414 totalOutputFramesToProcess = *pFrameCountOut;
30415 } else {
30416 /* Seek by input frames. */
30417 MA_ASSERT(pFrameCountIn != NULL);
30418 totalOutputFramesToProcess = ma_resampler_get_expected_output_frame_count(pResampler, *pFrameCountIn);
30419 }
30420
30421 if (pFramesIn != NULL) {
30422 /* Process input data. */
30423 MA_ASSERT(pFrameCountIn != NULL);
30424 while (totalOutputFramesProcessed < totalOutputFramesToProcess && totalInputFramesProcessed < *pFrameCountIn) {
30425 ma_uint64 inputFramesToProcessThisIteration = (*pFrameCountIn - totalInputFramesProcessed);
30426 ma_uint64 outputFramesToProcessThisIteration = (totalOutputFramesToProcess - totalOutputFramesProcessed);
30427 if (outputFramesToProcessThisIteration > sizeof(devnull) / bpf) {
30428 outputFramesToProcessThisIteration = sizeof(devnull) / bpf;
30429 }
30430
30431 result = ma_resampler_process_pcm_frames__read(pResampler, ma_offset_ptr(pFramesIn, totalInputFramesProcessed*bpf), &inputFramesToProcessThisIteration, ma_offset_ptr(devnull, totalOutputFramesProcessed*bpf), &outputFramesToProcessThisIteration);
30432 if (result != MA_SUCCESS) {
30433 return result;
30434 }
30435
30436 totalOutputFramesProcessed += outputFramesToProcessThisIteration;
30437 totalInputFramesProcessed += inputFramesToProcessThisIteration;
30438 }
30439 } else {
30440 /* Don't process input data - just update timing and filter state as if zeroes were passed in. */
30441 while (totalOutputFramesProcessed < totalOutputFramesToProcess) {
30442 ma_uint64 inputFramesToProcessThisIteration = 16384;
30443 ma_uint64 outputFramesToProcessThisIteration = (totalOutputFramesToProcess - totalOutputFramesProcessed);
30444 if (outputFramesToProcessThisIteration > sizeof(devnull) / bpf) {
30445 outputFramesToProcessThisIteration = sizeof(devnull) / bpf;
30446 }
30447
30448 result = ma_resampler_process_pcm_frames__read(pResampler, NULL, &inputFramesToProcessThisIteration, ma_offset_ptr(devnull, totalOutputFramesProcessed*bpf), &outputFramesToProcessThisIteration);
30449 if (result != MA_SUCCESS) {
30450 return result;
30451 }
30452
30453 totalOutputFramesProcessed += outputFramesToProcessThisIteration;
30454 totalInputFramesProcessed += inputFramesToProcessThisIteration;
30455 }
30456 }
30457
30458
30459 if (pFrameCountIn != NULL) {
30460 *pFrameCountIn = totalInputFramesProcessed;
30461 }
30462 if (pFrameCountOut != NULL) {
30463 *pFrameCountOut = totalOutputFramesProcessed;
30464 }
30465
30466 return MA_SUCCESS;
30467}
30468#endif
30469
30470static ma_result ma_resampler_process_pcm_frames__seek(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut)
30471{
30472 MA_ASSERT(pResampler != NULL);
30473
30474 switch (pResampler->config.algorithm)
30475 {
30476 case ma_resample_algorithm_linear:
30477 {
30478 return ma_resampler_process_pcm_frames__seek__linear(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut);
30479 } break;
30480
30481 case ma_resample_algorithm_speex:
30482 {
30483 #if defined(MA_HAS_SPEEX_RESAMPLER)
30484 return ma_resampler_process_pcm_frames__seek__speex(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut);
30485 #else
30486 break;
30487 #endif
30488 };
30489
30490 default: break;
30491 }
30492
30493 /* Should never hit this. */
30494 MA_ASSERT(MA_FALSE);
30495 return MA_INVALID_ARGS;
30496}
30497
30498
30499ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
30500{
30501 if (pResampler == NULL) {
30502 return MA_INVALID_ARGS;
30503 }
30504
30505 if (pFrameCountOut == NULL && pFrameCountIn == NULL) {
30506 return MA_INVALID_ARGS;
30507 }
30508
30509 if (pFramesOut != NULL) {
30510 /* Reading. */
30511 return ma_resampler_process_pcm_frames__read(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
30512 } else {
30513 /* Seeking. */
30514 return ma_resampler_process_pcm_frames__seek(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut);
30515 }
30516}
30517
30518ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
30519{
30520 if (pResampler == NULL) {
30521 return MA_INVALID_ARGS;
30522 }
30523
30524 if (sampleRateIn == 0 || sampleRateOut == 0) {
30525 return MA_INVALID_ARGS;
30526 }
30527
30528 pResampler->config.sampleRateIn = sampleRateIn;
30529 pResampler->config.sampleRateOut = sampleRateOut;
30530
30531 switch (pResampler->config.algorithm)
30532 {
30533 case ma_resample_algorithm_linear:
30534 {
30535 return ma_linear_resampler_set_rate(&pResampler->state.linear, sampleRateIn, sampleRateOut);
30536 } break;
30537
30538 case ma_resample_algorithm_speex:
30539 {
30540 #if defined(MA_HAS_SPEEX_RESAMPLER)
30541 return ma_result_from_speex_err(speex_resampler_set_rate((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, sampleRateIn, sampleRateOut));
30542 #else
30543 break;
30544 #endif
30545 };
30546
30547 default: break;
30548 }
30549
30550 /* Should never get here. */
30551 MA_ASSERT(MA_FALSE);
30552 return MA_INVALID_OPERATION;
30553}
30554
30555ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio)
30556{
30557 if (pResampler == NULL) {
30558 return MA_INVALID_ARGS;
30559 }
30560
30561 if (pResampler->config.algorithm == ma_resample_algorithm_linear) {
30562 return ma_linear_resampler_set_rate_ratio(&pResampler->state.linear, ratio);
30563 } else {
30564 /* Getting here means the backend does not have native support for setting the rate as a ratio so we just do it generically. */
30565 ma_uint32 n;
30566 ma_uint32 d;
30567
30568 d = 1000000; /* We use up to 6 decimal places. */
30569 n = (ma_uint32)(ratio * d);
30570
30571 if (n == 0) {
30572 return MA_INVALID_ARGS; /* Ratio too small. */
30573 }
30574
30575 MA_ASSERT(n != 0);
30576
30577 return ma_resampler_set_rate(pResampler, n, d);
30578 }
30579}
30580
30581ma_uint64 ma_resampler_get_required_input_frame_count(ma_resampler* pResampler, ma_uint64 outputFrameCount)
30582{
30583 if (pResampler == NULL) {
30584 return 0;
30585 }
30586
30587 if (outputFrameCount == 0) {
30588 return 0;
30589 }
30590
30591 switch (pResampler->config.algorithm)
30592 {
30593 case ma_resample_algorithm_linear:
30594 {
30595 return ma_linear_resampler_get_required_input_frame_count(&pResampler->state.linear, outputFrameCount);
30596 }
30597
30598 case ma_resample_algorithm_speex:
30599 {
30600 #if defined(MA_HAS_SPEEX_RESAMPLER)
30601 ma_uint64 count;
30602 int speexErr = ma_speex_resampler_get_required_input_frame_count((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, outputFrameCount, &count);
30603 if (speexErr != RESAMPLER_ERR_SUCCESS) {
30604 return 0;
30605 }
30606
30607 return count;
30608 #else
30609 break;
30610 #endif
30611 }
30612
30613 default: break;
30614 }
30615
30616 /* Should never get here. */
30617 MA_ASSERT(MA_FALSE);
30618 return 0;
30619}
30620
30621ma_uint64 ma_resampler_get_expected_output_frame_count(ma_resampler* pResampler, ma_uint64 inputFrameCount)
30622{
30623 if (pResampler == NULL) {
30624 return 0; /* Invalid args. */
30625 }
30626
30627 if (inputFrameCount == 0) {
30628 return 0;
30629 }
30630
30631 switch (pResampler->config.algorithm)
30632 {
30633 case ma_resample_algorithm_linear:
30634 {
30635 return ma_linear_resampler_get_expected_output_frame_count(&pResampler->state.linear, inputFrameCount);
30636 }
30637
30638 case ma_resample_algorithm_speex:
30639 {
30640 #if defined(MA_HAS_SPEEX_RESAMPLER)
30641 ma_uint64 count;
30642 int speexErr = ma_speex_resampler_get_expected_output_frame_count((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, inputFrameCount, &count);
30643 if (speexErr != RESAMPLER_ERR_SUCCESS) {
30644 return 0;
30645 }
30646
30647 return count;
30648 #else
30649 break;
30650 #endif
30651 }
30652
30653 default: break;
30654 }
30655
30656 /* Should never get here. */
30657 MA_ASSERT(MA_FALSE);
30658 return 0;
30659}
30660
30661ma_uint64 ma_resampler_get_input_latency(ma_resampler* pResampler)
30662{
30663 if (pResampler == NULL) {
30664 return 0;
30665 }
30666
30667 switch (pResampler->config.algorithm)
30668 {
30669 case ma_resample_algorithm_linear:
30670 {
30671 return ma_linear_resampler_get_input_latency(&pResampler->state.linear);
30672 }
30673
30674 case ma_resample_algorithm_speex:
30675 {
30676 #if defined(MA_HAS_SPEEX_RESAMPLER)
30677 return (ma_uint64)ma_speex_resampler_get_input_latency((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState);
30678 #else
30679 break;
30680 #endif
30681 }
30682
30683 default: break;
30684 }
30685
30686 /* Should never get here. */
30687 MA_ASSERT(MA_FALSE);
30688 return 0;
30689}
30690
30691ma_uint64 ma_resampler_get_output_latency(ma_resampler* pResampler)
30692{
30693 if (pResampler == NULL) {
30694 return 0;
30695 }
30696
30697 switch (pResampler->config.algorithm)
30698 {
30699 case ma_resample_algorithm_linear:
30700 {
30701 return ma_linear_resampler_get_output_latency(&pResampler->state.linear);
30702 }
30703
30704 case ma_resample_algorithm_speex:
30705 {
30706 #if defined(MA_HAS_SPEEX_RESAMPLER)
30707 return (ma_uint64)ma_speex_resampler_get_output_latency((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState);
30708 #else
30709 break;
30710 #endif
30711 }
30712
30713 default: break;
30714 }
30715
30716 /* Should never get here. */
30717 MA_ASSERT(MA_FALSE);
30718 return 0;
30719}
30720
30721/**************************************************************************************************************************************************************
30722
30723Channel Conversion
30724
30725**************************************************************************************************************************************************************/
30726#ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT
30727#define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12
30728#endif
30729
30730#define MA_PLANE_LEFT 0
30731#define MA_PLANE_RIGHT 1
30732#define MA_PLANE_FRONT 2
30733#define MA_PLANE_BACK 3
30734#define MA_PLANE_BOTTOM 4
30735#define MA_PLANE_TOP 5
30736
30737float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = {
30738 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */
30739 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */
30740 { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */
30741 { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */
30742 { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */
30743 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */
30744 { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */
30745 { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */
30746 { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */
30747 { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */
30748 { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */
30749 { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */
30750 { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */
30751 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */
30752 { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */
30753 { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */
30754 { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */
30755 { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */
30756 { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */
30757 { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */
30758 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */
30759 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */
30760 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */
30761 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */
30762 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */
30763 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */
30764 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */
30765 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */
30766 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */
30767 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */
30768 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */
30769 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */
30770 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */
30771 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */
30772 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */
30773 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */
30774 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */
30775 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */
30776 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */
30777 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */
30778 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */
30779 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */
30780 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */
30781 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */
30782 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */
30783 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */
30784 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */
30785 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */
30786 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */
30787 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */
30788 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */
30789 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */
30790};
30791
30792float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB)
30793{
30794 /*
30795 Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to
30796 the following output configuration:
30797
30798 - front/left
30799 - side/left
30800 - back/left
30801
30802 The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount
30803 of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated.
30804
30805 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
30806 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
30807 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
30808 receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between
30809 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
30810 across 3 spatial dimensions.
30811
30812 The first thing to do is figure out how each speaker's volume is spread over each of plane:
30813 - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane
30814 - side/left: 1 plane (left only) = 1/1 = entire volume from left plane
30815 - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane
30816 - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane
30817
30818 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
30819 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
30820 taken by the other to produce the final contribution.
30821 */
30822
30823 /* Contribution = Sum(Volume to Give * Volume to Take) */
30824 float contribution =
30825 g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] +
30826 g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] +
30827 g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] +
30828 g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] +
30829 g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] +
30830 g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5];
30831
30832 return contribution;
30833}
30834
30835ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel channelMapIn[MA_MAX_CHANNELS], ma_uint32 channelsOut, const ma_channel channelMapOut[MA_MAX_CHANNELS], ma_channel_mix_mode mixingMode)
30836{
30837 ma_channel_converter_config config;
30838 MA_ZERO_OBJECT(&config);
30839 config.format = format;
30840 config.channelsIn = channelsIn;
30841 config.channelsOut = channelsOut;
30842 ma_channel_map_copy(config.channelMapIn, channelMapIn, channelsIn);
30843 ma_channel_map_copy(config.channelMapOut, channelMapOut, channelsOut);
30844 config.mixingMode = mixingMode;
30845
30846 return config;
30847}
30848
30849static ma_int32 ma_channel_converter_float_to_fp(float x)
30850{
30851 return (ma_int32)(x * (1<<MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT));
30852}
30853
30854static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition)
30855{
30856 int i;
30857
30858 if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) {
30859 return MA_FALSE;
30860 }
30861
30862 for (i = 0; i < 6; ++i) { /* Each side of a cube. */
30863 if (g_maChannelPlaneRatios[channelPosition][i] != 0) {
30864 return MA_TRUE;
30865 }
30866 }
30867
30868 return MA_FALSE;
30869}
30870
30871ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, ma_channel_converter* pConverter)
30872{
30873 ma_uint32 iChannelIn;
30874 ma_uint32 iChannelOut;
30875
30876 if (pConverter == NULL) {
30877 return MA_INVALID_ARGS;
30878 }
30879
30880 MA_ZERO_OBJECT(pConverter);
30881
30882 if (pConfig == NULL) {
30883 return MA_INVALID_ARGS;
30884 }
30885
30886 if (!ma_channel_map_valid(pConfig->channelsIn, pConfig->channelMapIn)) {
30887 return MA_INVALID_ARGS; /* Invalid input channel map. */
30888 }
30889 if (!ma_channel_map_valid(pConfig->channelsOut, pConfig->channelMapOut)) {
30890 return MA_INVALID_ARGS; /* Invalid output channel map. */
30891 }
30892
30893 if (pConfig->format != ma_format_s16 && pConfig->format != ma_format_f32) {
30894 return MA_INVALID_ARGS; /* Invalid format. */
30895 }
30896
30897 pConverter->format = pConfig->format;
30898 pConverter->channelsIn = pConfig->channelsIn;
30899 pConverter->channelsOut = pConfig->channelsOut;
30900 ma_channel_map_copy(pConverter->channelMapIn, pConfig->channelMapIn, pConfig->channelsIn);
30901 ma_channel_map_copy(pConverter->channelMapOut, pConfig->channelMapOut, pConfig->channelsOut);
30902 pConverter->mixingMode = pConfig->mixingMode;
30903
30904 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
30905 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
30906 if (pConverter->format == ma_format_s16) {
30907 pConverter->weights.f32[iChannelIn][iChannelOut] = pConfig->weights[iChannelIn][iChannelOut];
30908 } else {
30909 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fp(pConfig->weights[iChannelIn][iChannelOut]);
30910 }
30911 }
30912 }
30913
30914
30915
30916 /* If the input and output channels and channel maps are the same we should use a passthrough. */
30917 if (pConverter->channelsIn == pConverter->channelsOut) {
30918 if (ma_channel_map_equal(pConverter->channelsIn, pConverter->channelMapIn, pConverter->channelMapOut)) {
30919 pConverter->isPassthrough = MA_TRUE;
30920 }
30921 if (ma_channel_map_blank(pConverter->channelsIn, pConverter->channelMapIn) || ma_channel_map_blank(pConverter->channelsOut, pConverter->channelMapOut)) {
30922 pConverter->isPassthrough = MA_TRUE;
30923 }
30924 }
30925
30926
30927 /*
30928 We can use a simple case for expanding the mono channel. This will used when expanding a mono input into any output so long
30929 as no LFE is present in the output.
30930 */
30931 if (!pConverter->isPassthrough) {
30932 if (pConverter->channelsIn == 1 && pConverter->channelMapIn[0] == MA_CHANNEL_MONO) {
30933 /* Optimal case if no LFE is in the output channel map. */
30934 pConverter->isSimpleMonoExpansion = MA_TRUE;
30935 if (ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->channelMapOut, MA_CHANNEL_LFE)) {
30936 pConverter->isSimpleMonoExpansion = MA_FALSE;
30937 }
30938 }
30939 }
30940
30941 /* Another optimized case is stereo to mono. */
30942 if (!pConverter->isPassthrough) {
30943 if (pConverter->channelsOut == 1 && pConverter->channelMapOut[0] == MA_CHANNEL_MONO && pConverter->channelsIn == 2) {
30944 /* Optimal case if no LFE is in the input channel map. */
30945 pConverter->isStereoToMono = MA_TRUE;
30946 if (ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->channelMapIn, MA_CHANNEL_LFE)) {
30947 pConverter->isStereoToMono = MA_FALSE;
30948 }
30949 }
30950 }
30951
30952
30953 /*
30954 Here is where we do a bit of pre-processing to know how each channel should be combined to make up the output. Rules:
30955
30956 1) If it's a passthrough, do nothing - it's just a simple memcpy().
30957 2) If the channel counts are the same and every channel position in the input map is present in the output map, use a
30958 simple shuffle. An example might be different 5.1 channel layouts.
30959 3) Otherwise channels are blended based on spatial locality.
30960 */
30961 if (!pConverter->isPassthrough) {
30962 if (pConverter->channelsIn == pConverter->channelsOut) {
30963 ma_bool32 areAllChannelPositionsPresent = MA_TRUE;
30964 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
30965 ma_bool32 isInputChannelPositionInOutput = MA_FALSE;
30966 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
30967 if (pConverter->channelMapIn[iChannelIn] == pConverter->channelMapOut[iChannelOut]) {
30968 isInputChannelPositionInOutput = MA_TRUE;
30969 break;
30970 }
30971 }
30972
30973 if (!isInputChannelPositionInOutput) {
30974 areAllChannelPositionsPresent = MA_FALSE;
30975 break;
30976 }
30977 }
30978
30979 if (areAllChannelPositionsPresent) {
30980 pConverter->isSimpleShuffle = MA_TRUE;
30981
30982 /*
30983 All the router will be doing is rearranging channels which means all we need to do is use a shuffling table which is just
30984 a mapping between the index of the input channel to the index of the output channel.
30985 */
30986 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
30987 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
30988 if (pConverter->channelMapIn[iChannelIn] == pConverter->channelMapOut[iChannelOut]) {
30989 pConverter->shuffleTable[iChannelIn] = (ma_uint8)iChannelOut;
30990 break;
30991 }
30992 }
30993 }
30994 }
30995 }
30996 }
30997
30998
30999 /*
31000 Here is where weights are calculated. Note that we calculate the weights at all times, even when using a passthrough and simple
31001 shuffling. We use different algorithms for calculating weights depending on our mixing mode.
31002
31003 In simple mode we don't do any blending (except for converting between mono, which is done in a later step). Instead we just
31004 map 1:1 matching channels. In this mode, if no channels in the input channel map correspond to anything in the output channel
31005 map, nothing will be heard!
31006 */
31007
31008 /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */
31009 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
31010 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
31011
31012 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
31013 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
31014
31015 if (channelPosIn == channelPosOut) {
31016 if (pConverter->format == ma_format_s16) {
31017 pConverter->weights.s16[iChannelIn][iChannelOut] = (1 << MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT);
31018 } else {
31019 pConverter->weights.f32[iChannelIn][iChannelOut] = 1;
31020 }
31021 }
31022 }
31023 }
31024
31025 /*
31026 The mono channel is accumulated on all other channels, except LFE. Make sure in this loop we exclude output mono channels since
31027 they were handled in the pass above.
31028 */
31029 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
31030 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
31031
31032 if (channelPosIn == MA_CHANNEL_MONO) {
31033 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
31034 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
31035
31036 if (channelPosOut != MA_CHANNEL_NONE && channelPosOut != MA_CHANNEL_MONO && channelPosOut != MA_CHANNEL_LFE) {
31037 if (pConverter->format == ma_format_s16) {
31038 pConverter->weights.s16[iChannelIn][iChannelOut] = (1 << MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT);
31039 } else {
31040 pConverter->weights.f32[iChannelIn][iChannelOut] = 1;
31041 }
31042 }
31043 }
31044 }
31045 }
31046
31047 /* The output mono channel is the average of all non-none, non-mono and non-lfe input channels. */
31048 {
31049 ma_uint32 len = 0;
31050 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
31051 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
31052
31053 if (channelPosIn != MA_CHANNEL_NONE && channelPosIn != MA_CHANNEL_MONO && channelPosIn != MA_CHANNEL_LFE) {
31054 len += 1;
31055 }
31056 }
31057
31058 if (len > 0) {
31059 float monoWeight = 1.0f / len;
31060
31061 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
31062 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
31063
31064 if (channelPosOut == MA_CHANNEL_MONO) {
31065 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
31066 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
31067
31068 if (channelPosIn != MA_CHANNEL_NONE && channelPosIn != MA_CHANNEL_MONO && channelPosIn != MA_CHANNEL_LFE) {
31069 if (pConverter->format == ma_format_s16) {
31070 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fp(monoWeight);
31071 } else {
31072 pConverter->weights.f32[iChannelIn][iChannelOut] = monoWeight;
31073 }
31074 }
31075 }
31076 }
31077 }
31078 }
31079 }
31080
31081
31082 /* Input and output channels that are not present on the other side need to be blended in based on spatial locality. */
31083 switch (pConverter->mixingMode)
31084 {
31085 case ma_channel_mix_mode_rectangular:
31086 {
31087 /* Unmapped input channels. */
31088 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
31089 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
31090
31091 if (ma_is_spatial_channel_position(channelPosIn)) {
31092 if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->channelMapOut, channelPosIn)) {
31093 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
31094 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
31095
31096 if (ma_is_spatial_channel_position(channelPosOut)) {
31097 float weight = 0;
31098 if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
31099 weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
31100 }
31101
31102 /* Only apply the weight if we haven't already got some contribution from the respective channels. */
31103 if (pConverter->format == ma_format_s16) {
31104 if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
31105 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fp(weight);
31106 }
31107 } else {
31108 if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
31109 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
31110 }
31111 }
31112 }
31113 }
31114 }
31115 }
31116 }
31117
31118 /* Unmapped output channels. */
31119 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
31120 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
31121
31122 if (ma_is_spatial_channel_position(channelPosOut)) {
31123 if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->channelMapIn, channelPosOut)) {
31124 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
31125 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
31126
31127 if (ma_is_spatial_channel_position(channelPosIn)) {
31128 float weight = 0;
31129 if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
31130 weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
31131 }
31132
31133 /* Only apply the weight if we haven't already got some contribution from the respective channels. */
31134 if (pConverter->format == ma_format_s16) {
31135 if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
31136 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fp(weight);
31137 }
31138 } else {
31139 if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
31140 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
31141 }
31142 }
31143 }
31144 }
31145 }
31146 }
31147 }
31148 } break;
31149
31150 case ma_channel_mix_mode_custom_weights:
31151 case ma_channel_mix_mode_simple:
31152 default:
31153 {
31154 /* Fallthrough. */
31155 } break;
31156 }
31157
31158
31159 return MA_SUCCESS;
31160}
31161
31162void ma_channel_converter_uninit(ma_channel_converter* pConverter)
31163{
31164 if (pConverter == NULL) {
31165 return;
31166 }
31167}
31168
31169static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
31170{
31171 MA_ASSERT(pConverter != NULL);
31172 MA_ASSERT(pFramesOut != NULL);
31173 MA_ASSERT(pFramesIn != NULL);
31174
31175 ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
31176 return MA_SUCCESS;
31177}
31178
31179static ma_result ma_channel_converter_process_pcm_frames__simple_shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
31180{
31181 ma_uint32 iFrame;
31182 ma_uint32 iChannelIn;
31183
31184 MA_ASSERT(pConverter != NULL);
31185 MA_ASSERT(pFramesOut != NULL);
31186 MA_ASSERT(pFramesIn != NULL);
31187 MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut);
31188
31189 if (pConverter->format == ma_format_s16) {
31190 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
31191 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
31192
31193 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
31194 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
31195 pFramesOutS16[pConverter->shuffleTable[iChannelIn]] = pFramesInS16[iChannelIn];
31196 }
31197 }
31198 } else {
31199 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
31200 const float* pFramesInF32 = (const float*)pFramesIn;
31201
31202 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
31203 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
31204 pFramesOutF32[pConverter->shuffleTable[iChannelIn]] = pFramesInF32[iChannelIn];
31205 }
31206 }
31207 }
31208
31209 return MA_SUCCESS;
31210}
31211
31212static ma_result ma_channel_converter_process_pcm_frames__simple_mono_expansion(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
31213{
31214 ma_uint64 iFrame;
31215
31216 MA_ASSERT(pConverter != NULL);
31217 MA_ASSERT(pFramesOut != NULL);
31218 MA_ASSERT(pFramesIn != NULL);
31219
31220 if (pConverter->format == ma_format_s16) {
31221 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
31222 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
31223
31224 if (pConverter->channelsOut == 2) {
31225 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
31226 pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame];
31227 pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame];
31228 }
31229 } else {
31230 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
31231 ma_uint32 iChannel;
31232 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
31233 pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame];
31234 }
31235 }
31236 }
31237 } else {
31238 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
31239 const float* pFramesInF32 = (const float*)pFramesIn;
31240
31241 if (pConverter->channelsOut == 2) {
31242 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
31243 pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame];
31244 pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame];
31245 }
31246 } else {
31247 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
31248 ma_uint32 iChannel;
31249 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
31250 pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame];
31251 }
31252 }
31253 }
31254 }
31255
31256 return MA_SUCCESS;
31257}
31258
31259static ma_result ma_channel_converter_process_pcm_frames__stereo_to_mono(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
31260{
31261 ma_uint64 iFrame;
31262
31263 MA_ASSERT(pConverter != NULL);
31264 MA_ASSERT(pFramesOut != NULL);
31265 MA_ASSERT(pFramesIn != NULL);
31266 MA_ASSERT(pConverter->channelsIn == 2);
31267 MA_ASSERT(pConverter->channelsOut == 1);
31268
31269 if (pConverter->format == ma_format_s16) {
31270 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
31271 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
31272
31273 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
31274 pFramesOutS16[iFrame] = (ma_int16)(((ma_int32)pFramesInS16[iFrame*2+0] + (ma_int32)pFramesInS16[iFrame*2+1]) / 2);
31275 }
31276 } else {
31277 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
31278 const float* pFramesInF32 = (const float*)pFramesIn;
31279
31280 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
31281 pFramesOutF32[iFrame] = (pFramesInF32[iFrame*2+0] + pFramesInF32[iFrame*2+0]) * 0.5f;
31282 }
31283 }
31284
31285 return MA_SUCCESS;
31286}
31287
31288static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
31289{
31290 ma_uint32 iFrame;
31291 ma_uint32 iChannelIn;
31292 ma_uint32 iChannelOut;
31293
31294 MA_ASSERT(pConverter != NULL);
31295 MA_ASSERT(pFramesOut != NULL);
31296 MA_ASSERT(pFramesIn != NULL);
31297
31298 /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */
31299
31300 /* Clear. */
31301 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
31302
31303 /* Accumulate. */
31304 if (pConverter->format == ma_format_s16) {
31305 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
31306 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
31307
31308 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
31309 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
31310 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
31311 ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut];
31312 s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
31313
31314 pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767);
31315 }
31316 }
31317 }
31318 } else {
31319 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
31320 const float* pFramesInF32 = (const float*)pFramesIn;
31321
31322 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
31323 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
31324 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
31325 pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut];
31326 }
31327 }
31328 }
31329 }
31330
31331 return MA_SUCCESS;
31332}
31333
31334ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
31335{
31336 if (pConverter == NULL) {
31337 return MA_INVALID_ARGS;
31338 }
31339
31340 if (pFramesOut == NULL) {
31341 return MA_INVALID_ARGS;
31342 }
31343
31344 if (pFramesIn == NULL) {
31345 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
31346 return MA_SUCCESS;
31347 }
31348
31349 if (pConverter->isPassthrough) {
31350 return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount);
31351 } else if (pConverter->isSimpleShuffle) {
31352 return ma_channel_converter_process_pcm_frames__simple_shuffle(pConverter, pFramesOut, pFramesIn, frameCount);
31353 } else if (pConverter->isSimpleMonoExpansion) {
31354 return ma_channel_converter_process_pcm_frames__simple_mono_expansion(pConverter, pFramesOut, pFramesIn, frameCount);
31355 } else if (pConverter->isStereoToMono) {
31356 return ma_channel_converter_process_pcm_frames__stereo_to_mono(pConverter, pFramesOut, pFramesIn, frameCount);
31357 } else {
31358 return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount);
31359 }
31360}
31361
31362
31363/**************************************************************************************************************************************************************
31364
31365Data Conversion
31366
31367**************************************************************************************************************************************************************/
31368ma_data_converter_config ma_data_converter_config_init_default()
31369{
31370 ma_data_converter_config config;
31371 MA_ZERO_OBJECT(&config);
31372
31373 config.ditherMode = ma_dither_mode_none;
31374 config.resampling.algorithm = ma_resample_algorithm_linear;
31375 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. */
31376
31377 /* Linear resampling defaults. */
31378 config.resampling.linear.lpfCount = 1;
31379 config.resampling.linear.lpfNyquistFactor = 1;
31380
31381 /* Speex resampling defaults. */
31382 config.resampling.speex.quality = 3;
31383
31384 return config;
31385}
31386
31387ma_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)
31388{
31389 ma_data_converter_config config = ma_data_converter_config_init_default();
31390 config.formatIn = formatIn;
31391 config.formatOut = formatOut;
31392 config.channelsIn = channelsIn;
31393 config.channelsOut = channelsOut;
31394 config.sampleRateIn = sampleRateIn;
31395 config.sampleRateOut = sampleRateOut;
31396
31397 return config;
31398}
31399
31400ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, ma_data_converter* pConverter)
31401{
31402 ma_result result;
31403 ma_format midFormat;
31404
31405 if (pConverter == NULL) {
31406 return MA_INVALID_ARGS;
31407 }
31408
31409 MA_ZERO_OBJECT(pConverter);
31410
31411 if (pConfig == NULL) {
31412 return MA_INVALID_ARGS;
31413 }
31414
31415 pConverter->config = *pConfig;
31416
31417 /*
31418 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
31419 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
31420 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
31421 use f32.
31422 */
31423 /* */ if (pConverter->config.formatOut == ma_format_s16 || pConverter->config.formatOut == ma_format_f32) {
31424 midFormat = pConverter->config.formatOut;
31425 } else if (pConverter->config.formatIn == ma_format_s16 || pConverter->config.formatIn == ma_format_f32) {
31426 midFormat = pConverter->config.formatIn;
31427 } else {
31428 midFormat = ma_format_f32;
31429 }
31430
31431 if (pConverter->config.formatIn != midFormat) {
31432 pConverter->hasPreFormatConversion = MA_TRUE;
31433 }
31434 if (pConverter->config.formatOut != midFormat) {
31435 pConverter->hasPostFormatConversion = MA_TRUE;
31436 }
31437
31438
31439 /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */
31440 {
31441 ma_uint32 iChannelIn;
31442 ma_uint32 iChannelOut;
31443 ma_channel_converter_config channelConverterConfig;
31444
31445 channelConverterConfig = ma_channel_converter_config_init(midFormat, pConverter->config.channelsIn, pConverter->config.channelMapIn, pConverter->config.channelsOut, pConverter->config.channelMapOut, pConverter->config.channelMixMode);
31446
31447 /* Channel weights. */
31448 for (iChannelIn = 0; iChannelIn < pConverter->config.channelsIn; iChannelIn += 1) {
31449 for (iChannelOut = 0; iChannelOut < pConverter->config.channelsOut; iChannelOut += 1) {
31450 channelConverterConfig.weights[iChannelIn][iChannelOut] = pConverter->config.channelWeights[iChannelIn][iChannelOut];
31451 }
31452 }
31453
31454 result = ma_channel_converter_init(&channelConverterConfig, &pConverter->channelConverter);
31455 if (result != MA_SUCCESS) {
31456 return result;
31457 }
31458
31459 /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */
31460 if (pConverter->channelConverter.isPassthrough == MA_FALSE) {
31461 pConverter->hasChannelConverter = MA_TRUE;
31462 }
31463 }
31464
31465
31466 /* 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. */
31467 if (pConverter->config.resampling.allowDynamicSampleRate == MA_FALSE) {
31468 pConverter->config.resampling.allowDynamicSampleRate = pConverter->config.sampleRateIn != pConverter->config.sampleRateOut;
31469 }
31470
31471 /* Resampler. */
31472 if (pConverter->config.resampling.allowDynamicSampleRate) {
31473 ma_resampler_config resamplerConfig;
31474 ma_uint32 resamplerChannels;
31475
31476 /* 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. */
31477 if (pConverter->config.channelsIn < pConverter->config.channelsOut) {
31478 resamplerChannels = pConverter->config.channelsIn;
31479 } else {
31480 resamplerChannels = pConverter->config.channelsOut;
31481 }
31482
31483 resamplerConfig = ma_resampler_config_init(midFormat, resamplerChannels, pConverter->config.sampleRateIn, pConverter->config.sampleRateOut, pConverter->config.resampling.algorithm);
31484 resamplerConfig.linear.lpfCount = pConverter->config.resampling.linear.lpfCount;
31485 resamplerConfig.linear.lpfNyquistFactor = pConverter->config.resampling.linear.lpfNyquistFactor;
31486 resamplerConfig.speex.quality = pConverter->config.resampling.speex.quality;
31487
31488 result = ma_resampler_init(&resamplerConfig, &pConverter->resampler);
31489 if (result != MA_SUCCESS) {
31490 return result;
31491 }
31492
31493 pConverter->hasResampler = MA_TRUE;
31494 }
31495
31496 /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */
31497 if (pConverter->hasPreFormatConversion == MA_FALSE &&
31498 pConverter->hasPostFormatConversion == MA_FALSE &&
31499 pConverter->hasChannelConverter == MA_FALSE &&
31500 pConverter->hasResampler == MA_FALSE) {
31501 pConverter->isPassthrough = MA_TRUE;
31502 }
31503
31504 return MA_SUCCESS;
31505}
31506
31507void ma_data_converter_uninit(ma_data_converter* pConverter)
31508{
31509 if (pConverter == NULL) {
31510 return;
31511 }
31512
31513 if (pConverter->hasResampler) {
31514 ma_resampler_uninit(&pConverter->resampler);
31515 }
31516}
31517
31518static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
31519{
31520 ma_uint64 frameCountIn;
31521 ma_uint64 frameCountOut;
31522 ma_uint64 frameCount;
31523
31524 MA_ASSERT(pConverter != NULL);
31525
31526 frameCountIn = 0;
31527 if (pFrameCountIn != NULL) {
31528 frameCountIn = *pFrameCountIn;
31529 }
31530
31531 frameCountOut = 0;
31532 if (pFrameCountOut != NULL) {
31533 frameCountOut = *pFrameCountOut;
31534 }
31535
31536 frameCount = ma_min(frameCountIn, frameCountOut);
31537
31538 if (pFramesOut != NULL) {
31539 if (pFramesIn != NULL) {
31540 ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
31541 } else {
31542 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
31543 }
31544 }
31545
31546 if (pFrameCountIn != NULL) {
31547 *pFrameCountIn = frameCount;
31548 }
31549 if (pFrameCountOut != NULL) {
31550 *pFrameCountOut = frameCount;
31551 }
31552
31553 return MA_SUCCESS;
31554}
31555
31556static 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)
31557{
31558 ma_uint64 frameCountIn;
31559 ma_uint64 frameCountOut;
31560 ma_uint64 frameCount;
31561
31562 MA_ASSERT(pConverter != NULL);
31563
31564 frameCountIn = 0;
31565 if (pFrameCountIn != NULL) {
31566 frameCountIn = *pFrameCountIn;
31567 }
31568
31569 frameCountOut = 0;
31570 if (pFrameCountOut != NULL) {
31571 frameCountOut = *pFrameCountOut;
31572 }
31573
31574 frameCount = ma_min(frameCountIn, frameCountOut);
31575
31576 if (pFramesOut != NULL) {
31577 if (pFramesIn != NULL) {
31578 ma_convert_pcm_frames_format(pFramesOut, pConverter->config.formatOut, pFramesIn, pConverter->config.formatIn, frameCount, pConverter->config.channelsIn, pConverter->config.ditherMode);
31579 } else {
31580 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
31581 }
31582 }
31583
31584 if (pFrameCountIn != NULL) {
31585 *pFrameCountIn = frameCount;
31586 }
31587 if (pFrameCountOut != NULL) {
31588 *pFrameCountOut = frameCount;
31589 }
31590
31591 return MA_SUCCESS;
31592}
31593
31594
31595static 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)
31596{
31597 ma_result result = MA_SUCCESS;
31598 ma_uint64 frameCountIn;
31599 ma_uint64 frameCountOut;
31600 ma_uint64 framesProcessedIn;
31601 ma_uint64 framesProcessedOut;
31602
31603 MA_ASSERT(pConverter != NULL);
31604
31605 frameCountIn = 0;
31606 if (pFrameCountIn != NULL) {
31607 frameCountIn = *pFrameCountIn;
31608 }
31609
31610 frameCountOut = 0;
31611 if (pFrameCountOut != NULL) {
31612 frameCountOut = *pFrameCountOut;
31613 }
31614
31615 framesProcessedIn = 0;
31616 framesProcessedOut = 0;
31617
31618 while (framesProcessedOut < frameCountOut) {
31619 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
31620 const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
31621 const void* pFramesInThisIteration;
31622 /* */ void* pFramesOutThisIteration;
31623 ma_uint64 frameCountInThisIteration;
31624 ma_uint64 frameCountOutThisIteration;
31625
31626 if (pFramesIn != NULL) {
31627 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn));
31628 } else {
31629 pFramesInThisIteration = NULL;
31630 }
31631
31632 if (pFramesOut != NULL) {
31633 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
31634 } else {
31635 pFramesOutThisIteration = NULL;
31636 }
31637
31638 /* Do a pre format conversion if necessary. */
31639 if (pConverter->hasPreFormatConversion) {
31640 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
31641 const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
31642
31643 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
31644 if (frameCountInThisIteration > tempBufferInCap) {
31645 frameCountInThisIteration = tempBufferInCap;
31646 }
31647
31648 if (pConverter->hasPostFormatConversion) {
31649 if (frameCountInThisIteration > tempBufferOutCap) {
31650 frameCountInThisIteration = tempBufferOutCap;
31651 }
31652 }
31653
31654 if (pFramesInThisIteration != NULL) {
31655 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.config.format, pFramesInThisIteration, pConverter->config.formatIn, frameCountInThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode);
31656 } else {
31657 MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
31658 }
31659
31660 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
31661
31662 if (pConverter->hasPostFormatConversion) {
31663 /* Both input and output conversion required. Output to the temp buffer. */
31664 if (frameCountOutThisIteration > tempBufferOutCap) {
31665 frameCountOutThisIteration = tempBufferOutCap;
31666 }
31667
31668 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
31669 } else {
31670 /* Only pre-format required. Output straight to the output buffer. */
31671 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration);
31672 }
31673
31674 if (result != MA_SUCCESS) {
31675 break;
31676 }
31677 } else {
31678 /* No pre-format required. Just read straight from the input buffer. */
31679 MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
31680
31681 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
31682 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
31683 if (frameCountOutThisIteration > tempBufferOutCap) {
31684 frameCountOutThisIteration = tempBufferOutCap;
31685 }
31686
31687 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
31688 if (result != MA_SUCCESS) {
31689 break;
31690 }
31691 }
31692
31693 /* If we are doing a post format conversion we need to do that now. */
31694 if (pConverter->hasPostFormatConversion) {
31695 if (pFramesOutThisIteration != NULL) {
31696 ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->config.formatOut, pTempBufferOut, pConverter->resampler.config.format, frameCountOutThisIteration, pConverter->resampler.config.channels, pConverter->config.ditherMode);
31697 }
31698 }
31699
31700 framesProcessedIn += frameCountInThisIteration;
31701 framesProcessedOut += frameCountOutThisIteration;
31702
31703 MA_ASSERT(framesProcessedIn <= frameCountIn);
31704 MA_ASSERT(framesProcessedOut <= frameCountOut);
31705
31706 if (frameCountOutThisIteration == 0) {
31707 break; /* Consumed all of our input data. */
31708 }
31709 }
31710
31711 if (pFrameCountIn != NULL) {
31712 *pFrameCountIn = framesProcessedIn;
31713 }
31714 if (pFrameCountOut != NULL) {
31715 *pFrameCountOut = framesProcessedOut;
31716 }
31717
31718 return result;
31719}
31720
31721static 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)
31722{
31723 MA_ASSERT(pConverter != NULL);
31724
31725 if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
31726 /* Neither pre- nor post-format required. This is simple case where only resampling is required. */
31727 return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
31728 } else {
31729 /* Format conversion required. */
31730 return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
31731 }
31732}
31733
31734static 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)
31735{
31736 ma_result result;
31737 ma_uint64 frameCountIn;
31738 ma_uint64 frameCountOut;
31739 ma_uint64 frameCount;
31740
31741 MA_ASSERT(pConverter != NULL);
31742
31743 frameCountIn = 0;
31744 if (pFrameCountIn != NULL) {
31745 frameCountIn = *pFrameCountIn;
31746 }
31747
31748 frameCountOut = 0;
31749 if (pFrameCountOut != NULL) {
31750 frameCountOut = *pFrameCountOut;
31751 }
31752
31753 frameCount = ma_min(frameCountIn, frameCountOut);
31754
31755 if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
31756 /* No format conversion required. */
31757 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount);
31758 if (result != MA_SUCCESS) {
31759 return result;
31760 }
31761 } else {
31762 /* Format conversion required. */
31763 ma_uint64 framesProcessed = 0;
31764
31765 while (framesProcessed < frameCount) {
31766 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
31767 const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
31768 const void* pFramesInThisIteration;
31769 /* */ void* pFramesOutThisIteration;
31770 ma_uint64 frameCountThisIteration;
31771
31772 if (pFramesIn != NULL) {
31773 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn));
31774 } else {
31775 pFramesInThisIteration = NULL;
31776 }
31777
31778 if (pFramesOut != NULL) {
31779 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
31780 } else {
31781 pFramesOutThisIteration = NULL;
31782 }
31783
31784 /* Do a pre format conversion if necessary. */
31785 if (pConverter->hasPreFormatConversion) {
31786 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
31787 const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
31788
31789 frameCountThisIteration = (frameCount - framesProcessed);
31790 if (frameCountThisIteration > tempBufferInCap) {
31791 frameCountThisIteration = tempBufferInCap;
31792 }
31793
31794 if (pConverter->hasPostFormatConversion) {
31795 if (frameCountThisIteration > tempBufferOutCap) {
31796 frameCountThisIteration = tempBufferOutCap;
31797 }
31798 }
31799
31800 if (pFramesInThisIteration != NULL) {
31801 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->config.formatIn, frameCountThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode);
31802 } else {
31803 MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
31804 }
31805
31806 if (pConverter->hasPostFormatConversion) {
31807 /* Both input and output conversion required. Output to the temp buffer. */
31808 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration);
31809 } else {
31810 /* Only pre-format required. Output straight to the output buffer. */
31811 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration);
31812 }
31813
31814 if (result != MA_SUCCESS) {
31815 break;
31816 }
31817 } else {
31818 /* No pre-format required. Just read straight from the input buffer. */
31819 MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
31820
31821 frameCountThisIteration = (frameCount - framesProcessed);
31822 if (frameCountThisIteration > tempBufferOutCap) {
31823 frameCountThisIteration = tempBufferOutCap;
31824 }
31825
31826 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration);
31827 if (result != MA_SUCCESS) {
31828 break;
31829 }
31830 }
31831
31832 /* If we are doing a post format conversion we need to do that now. */
31833 if (pConverter->hasPostFormatConversion) {
31834 if (pFramesOutThisIteration != NULL) {
31835 ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->config.formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->config.ditherMode);
31836 }
31837 }
31838
31839 framesProcessed += frameCountThisIteration;
31840 }
31841 }
31842
31843 if (pFrameCountIn != NULL) {
31844 *pFrameCountIn = frameCount;
31845 }
31846 if (pFrameCountOut != NULL) {
31847 *pFrameCountOut = frameCount;
31848 }
31849
31850 return MA_SUCCESS;
31851}
31852
31853static 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)
31854{
31855 ma_result result;
31856 ma_uint64 frameCountIn;
31857 ma_uint64 frameCountOut;
31858 ma_uint64 framesProcessedIn;
31859 ma_uint64 framesProcessedOut;
31860 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */
31861 ma_uint64 tempBufferInCap;
31862 ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */
31863 ma_uint64 tempBufferMidCap;
31864 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */
31865 ma_uint64 tempBufferOutCap;
31866
31867 MA_ASSERT(pConverter != NULL);
31868 MA_ASSERT(pConverter->resampler.config.format == pConverter->channelConverter.format);
31869 MA_ASSERT(pConverter->resampler.config.channels == pConverter->channelConverter.channelsIn);
31870 MA_ASSERT(pConverter->resampler.config.channels < pConverter->channelConverter.channelsOut);
31871
31872 frameCountIn = 0;
31873 if (pFrameCountIn != NULL) {
31874 frameCountIn = *pFrameCountIn;
31875 }
31876
31877 frameCountOut = 0;
31878 if (pFrameCountOut != NULL) {
31879 frameCountOut = *pFrameCountOut;
31880 }
31881
31882 framesProcessedIn = 0;
31883 framesProcessedOut = 0;
31884
31885 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
31886 tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
31887 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
31888
31889 while (framesProcessedOut < frameCountOut) {
31890 ma_uint64 frameCountInThisIteration;
31891 ma_uint64 frameCountOutThisIteration;
31892 const void* pRunningFramesIn = NULL;
31893 void* pRunningFramesOut = NULL;
31894 const void* pResampleBufferIn;
31895 void* pChannelsBufferOut;
31896
31897 if (pFramesIn != NULL) {
31898 pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn));
31899 }
31900 if (pFramesOut != NULL) {
31901 pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
31902 }
31903
31904 /* Run input data through the resampler and output it to the temporary buffer. */
31905 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
31906
31907 if (pConverter->hasPreFormatConversion) {
31908 if (frameCountInThisIteration > tempBufferInCap) {
31909 frameCountInThisIteration = tempBufferInCap;
31910 }
31911 }
31912
31913 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
31914 if (frameCountOutThisIteration > tempBufferMidCap) {
31915 frameCountOutThisIteration = tempBufferMidCap;
31916 }
31917
31918 /* We can't read more frames than can fit in the output buffer. */
31919 if (pConverter->hasPostFormatConversion) {
31920 if (frameCountOutThisIteration > tempBufferOutCap) {
31921 frameCountOutThisIteration = tempBufferOutCap;
31922 }
31923 }
31924
31925 /* 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. */
31926 {
31927 ma_uint64 requiredInputFrameCount = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration);
31928 if (frameCountInThisIteration > requiredInputFrameCount) {
31929 frameCountInThisIteration = requiredInputFrameCount;
31930 }
31931 }
31932
31933 if (pConverter->hasPreFormatConversion) {
31934 if (pFramesIn != NULL) {
31935 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.config.format, pRunningFramesIn, pConverter->config.formatIn, frameCountInThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode);
31936 pResampleBufferIn = pTempBufferIn;
31937 } else {
31938 pResampleBufferIn = NULL;
31939 }
31940 } else {
31941 pResampleBufferIn = pRunningFramesIn;
31942 }
31943
31944 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration);
31945 if (result != MA_SUCCESS) {
31946 return result;
31947 }
31948
31949
31950 /*
31951 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
31952 this part if we have an output buffer.
31953 */
31954 if (pFramesOut != NULL) {
31955 if (pConverter->hasPostFormatConversion) {
31956 pChannelsBufferOut = pTempBufferOut;
31957 } else {
31958 pChannelsBufferOut = pRunningFramesOut;
31959 }
31960
31961 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration);
31962 if (result != MA_SUCCESS) {
31963 return result;
31964 }
31965
31966 /* Finally we do post format conversion. */
31967 if (pConverter->hasPostFormatConversion) {
31968 ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->config.formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->config.ditherMode);
31969 }
31970 }
31971
31972
31973 framesProcessedIn += frameCountInThisIteration;
31974 framesProcessedOut += frameCountOutThisIteration;
31975
31976 MA_ASSERT(framesProcessedIn <= frameCountIn);
31977 MA_ASSERT(framesProcessedOut <= frameCountOut);
31978
31979 if (frameCountOutThisIteration == 0) {
31980 break; /* Consumed all of our input data. */
31981 }
31982 }
31983
31984 if (pFrameCountIn != NULL) {
31985 *pFrameCountIn = framesProcessedIn;
31986 }
31987 if (pFrameCountOut != NULL) {
31988 *pFrameCountOut = framesProcessedOut;
31989 }
31990
31991 return MA_SUCCESS;
31992}
31993
31994static 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)
31995{
31996 ma_result result;
31997 ma_uint64 frameCountIn;
31998 ma_uint64 frameCountOut;
31999 ma_uint64 framesProcessedIn;
32000 ma_uint64 framesProcessedOut;
32001 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */
32002 ma_uint64 tempBufferInCap;
32003 ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */
32004 ma_uint64 tempBufferMidCap;
32005 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */
32006 ma_uint64 tempBufferOutCap;
32007
32008 MA_ASSERT(pConverter != NULL);
32009 MA_ASSERT(pConverter->resampler.config.format == pConverter->channelConverter.format);
32010 MA_ASSERT(pConverter->resampler.config.channels == pConverter->channelConverter.channelsOut);
32011 MA_ASSERT(pConverter->resampler.config.channels < pConverter->channelConverter.channelsIn);
32012
32013 frameCountIn = 0;
32014 if (pFrameCountIn != NULL) {
32015 frameCountIn = *pFrameCountIn;
32016 }
32017
32018 frameCountOut = 0;
32019 if (pFrameCountOut != NULL) {
32020 frameCountOut = *pFrameCountOut;
32021 }
32022
32023 framesProcessedIn = 0;
32024 framesProcessedOut = 0;
32025
32026 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
32027 tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
32028 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
32029
32030 while (framesProcessedOut < frameCountOut) {
32031 ma_uint64 frameCountInThisIteration;
32032 ma_uint64 frameCountOutThisIteration;
32033 const void* pRunningFramesIn = NULL;
32034 void* pRunningFramesOut = NULL;
32035 const void* pChannelsBufferIn;
32036 void* pResampleBufferOut;
32037
32038 if (pFramesIn != NULL) {
32039 pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn));
32040 }
32041 if (pFramesOut != NULL) {
32042 pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
32043 }
32044
32045 /* Run input data through the channel converter and output it to the temporary buffer. */
32046 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
32047
32048 if (pConverter->hasPreFormatConversion) {
32049 if (frameCountInThisIteration > tempBufferInCap) {
32050 frameCountInThisIteration = tempBufferInCap;
32051 }
32052
32053 if (pRunningFramesIn != NULL) {
32054 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->config.formatIn, frameCountInThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode);
32055 pChannelsBufferIn = pTempBufferIn;
32056 } else {
32057 pChannelsBufferIn = NULL;
32058 }
32059 } else {
32060 pChannelsBufferIn = pRunningFramesIn;
32061 }
32062
32063 /*
32064 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
32065 in this case which means we should always have capacity, but I'm leaving it here just for safety for future maintenance.
32066 */
32067 if (frameCountInThisIteration > tempBufferMidCap) {
32068 frameCountInThisIteration = tempBufferMidCap;
32069 }
32070
32071 /*
32072 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
32073 input samples and will end up glitching.
32074 */
32075 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
32076 if (frameCountOutThisIteration > tempBufferMidCap) {
32077 frameCountOutThisIteration = tempBufferMidCap;
32078 }
32079
32080 if (pConverter->hasPostFormatConversion) {
32081 ma_uint64 requiredInputFrameCount;
32082
32083 if (frameCountOutThisIteration > tempBufferOutCap) {
32084 frameCountOutThisIteration = tempBufferOutCap;
32085 }
32086
32087 requiredInputFrameCount = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration);
32088 if (frameCountInThisIteration > requiredInputFrameCount) {
32089 frameCountInThisIteration = requiredInputFrameCount;
32090 }
32091 }
32092
32093 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration);
32094 if (result != MA_SUCCESS) {
32095 return result;
32096 }
32097
32098
32099 /* At this point we have converted the channels to the output channel count which we now need to resample. */
32100 if (pConverter->hasPostFormatConversion) {
32101 pResampleBufferOut = pTempBufferOut;
32102 } else {
32103 pResampleBufferOut = pRunningFramesOut;
32104 }
32105
32106 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration);
32107 if (result != MA_SUCCESS) {
32108 return result;
32109 }
32110
32111 /* Finally we can do the post format conversion. */
32112 if (pConverter->hasPostFormatConversion) {
32113 if (pRunningFramesOut != NULL) {
32114 ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->config.formatOut, pResampleBufferOut, pConverter->resampler.config.format, frameCountOutThisIteration, pConverter->config.channelsOut, pConverter->config.ditherMode);
32115 }
32116 }
32117
32118 framesProcessedIn += frameCountInThisIteration;
32119 framesProcessedOut += frameCountOutThisIteration;
32120
32121 MA_ASSERT(framesProcessedIn <= frameCountIn);
32122 MA_ASSERT(framesProcessedOut <= frameCountOut);
32123
32124 if (frameCountOutThisIteration == 0) {
32125 break; /* Consumed all of our input data. */
32126 }
32127 }
32128
32129 if (pFrameCountIn != NULL) {
32130 *pFrameCountIn = framesProcessedIn;
32131 }
32132 if (pFrameCountOut != NULL) {
32133 *pFrameCountOut = framesProcessedOut;
32134 }
32135
32136 return MA_SUCCESS;
32137}
32138
32139ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
32140{
32141 if (pConverter == NULL) {
32142 return MA_INVALID_ARGS;
32143 }
32144
32145 if (pConverter->isPassthrough) {
32146 return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
32147 }
32148
32149 /*
32150 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
32151 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
32152 that it has less work (resampling is the most expensive part of format conversion).
32153 */
32154 if (pConverter->config.channelsIn < pConverter->config.channelsOut) {
32155 /* Do resampling first, if necessary. */
32156 MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE);
32157
32158 if (pConverter->hasResampler) {
32159 /* Resampling first. */
32160 return ma_data_converter_process_pcm_frames__resampling_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
32161 } else {
32162 /* Resampling not required. */
32163 return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
32164 }
32165 } else {
32166 /* Do channel conversion first, if necessary. */
32167 if (pConverter->hasChannelConverter) {
32168 if (pConverter->hasResampler) {
32169 /* Channel routing first. */
32170 return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
32171 } else {
32172 /* Resampling not required. */
32173 return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
32174 }
32175 } else {
32176 /* Channel routing not required. */
32177 if (pConverter->hasResampler) {
32178 /* Resampling only. */
32179 return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
32180 } else {
32181 /* No channel routing nor resampling required. Just format conversion. */
32182 return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
32183 }
32184 }
32185 }
32186}
32187
32188ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
32189{
32190 if (pConverter == NULL) {
32191 return MA_INVALID_ARGS;
32192 }
32193
32194 if (pConverter->hasResampler == MA_FALSE) {
32195 return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */
32196 }
32197
32198 return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut);
32199}
32200
32201ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut)
32202{
32203 if (pConverter == NULL) {
32204 return MA_INVALID_ARGS;
32205 }
32206
32207 if (pConverter->hasResampler == MA_FALSE) {
32208 return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */
32209 }
32210
32211 return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut);
32212}
32213
32214ma_uint64 ma_data_converter_get_required_input_frame_count(ma_data_converter* pConverter, ma_uint64 outputFrameCount)
32215{
32216 if (pConverter == NULL) {
32217 return 0;
32218 }
32219
32220 if (pConverter->hasResampler) {
32221 return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount);
32222 } else {
32223 return outputFrameCount; /* 1:1 */
32224 }
32225}
32226
32227ma_uint64 ma_data_converter_get_expected_output_frame_count(ma_data_converter* pConverter, ma_uint64 inputFrameCount)
32228{
32229 if (pConverter == NULL) {
32230 return 0;
32231 }
32232
32233 if (pConverter->hasResampler) {
32234 return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount);
32235 } else {
32236 return inputFrameCount; /* 1:1 */
32237 }
32238}
32239
32240ma_uint64 ma_data_converter_get_input_latency(ma_data_converter* pConverter)
32241{
32242 if (pConverter == NULL) {
32243 return 0;
32244 }
32245
32246 if (pConverter->hasResampler) {
32247 return ma_resampler_get_input_latency(&pConverter->resampler);
32248 }
32249
32250 return 0; /* No latency without a resampler. */
32251}
32252
32253ma_uint64 ma_data_converter_get_output_latency(ma_data_converter* pConverter)
32254{
32255 if (pConverter == NULL) {
32256 return 0;
32257 }
32258
32259 if (pConverter->hasResampler) {
32260 return ma_resampler_get_output_latency(&pConverter->resampler);
32261 }
32262
32263 return 0; /* No latency without a resampler. */
32264}
32265
32266
32267
32268/**************************************************************************************************************************************************************
32269
32270Format Conversion
32271
32272**************************************************************************************************************************************************************/
32273
32274/* u8 */
32275void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32276{
32277 (void)ditherMode;
32278 ma_copy_memory_64(dst, src, count * sizeof(ma_uint8));
32279}
32280
32281
32282static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32283{
32284 ma_int16* dst_s16 = (ma_int16*)dst;
32285 const ma_uint8* src_u8 = (const ma_uint8*)src;
32286
32287 ma_uint64 i;
32288 for (i = 0; i < count; i += 1) {
32289 ma_int16 x = src_u8[i];
32290 x = x - 128;
32291 x = x << 8;
32292 dst_s16[i] = x;
32293 }
32294
32295 (void)ditherMode;
32296}
32297
32298static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32299{
32300 ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
32301}
32302
32303#if defined(MA_SUPPORT_SSE2)
32304static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32305{
32306 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
32307}
32308#endif
32309#if defined(MA_SUPPORT_AVX2)
32310static MA_INLINE void ma_pcm_u8_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32311{
32312 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
32313}
32314#endif
32315#if defined(MA_SUPPORT_NEON)
32316static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32317{
32318 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
32319}
32320#endif
32321
32322void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32323{
32324#ifdef MA_USE_REFERENCE_CONVERSION_APIS
32325 ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
32326#else
32327 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
32328 if (ma_has_avx2()) {
32329 ma_pcm_u8_to_s16__avx2(dst, src, count, ditherMode);
32330 } else
32331 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
32332 if (ma_has_sse2()) {
32333 ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode);
32334 } else
32335 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
32336 if (ma_has_neon()) {
32337 ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode);
32338 } else
32339 #endif
32340 {
32341 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
32342 }
32343#endif
32344}
32345
32346
32347static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32348{
32349 ma_uint8* dst_s24 = (ma_uint8*)dst;
32350 const ma_uint8* src_u8 = (const ma_uint8*)src;
32351
32352 ma_uint64 i;
32353 for (i = 0; i < count; i += 1) {
32354 ma_int16 x = src_u8[i];
32355 x = x - 128;
32356
32357 dst_s24[i*3+0] = 0;
32358 dst_s24[i*3+1] = 0;
32359 dst_s24[i*3+2] = (ma_uint8)((ma_int8)x);
32360 }
32361
32362 (void)ditherMode;
32363}
32364
32365static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32366{
32367 ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
32368}
32369
32370#if defined(MA_SUPPORT_SSE2)
32371static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32372{
32373 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
32374}
32375#endif
32376#if defined(MA_SUPPORT_AVX2)
32377static MA_INLINE void ma_pcm_u8_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32378{
32379 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
32380}
32381#endif
32382#if defined(MA_SUPPORT_NEON)
32383static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32384{
32385 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
32386}
32387#endif
32388
32389void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32390{
32391#ifdef MA_USE_REFERENCE_CONVERSION_APIS
32392 ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
32393#else
32394 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
32395 if (ma_has_avx2()) {
32396 ma_pcm_u8_to_s24__avx2(dst, src, count, ditherMode);
32397 } else
32398 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
32399 if (ma_has_sse2()) {
32400 ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode);
32401 } else
32402 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
32403 if (ma_has_neon()) {
32404 ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode);
32405 } else
32406 #endif
32407 {
32408 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
32409 }
32410#endif
32411}
32412
32413
32414static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32415{
32416 ma_int32* dst_s32 = (ma_int32*)dst;
32417 const ma_uint8* src_u8 = (const ma_uint8*)src;
32418
32419 ma_uint64 i;
32420 for (i = 0; i < count; i += 1) {
32421 ma_int32 x = src_u8[i];
32422 x = x - 128;
32423 x = x << 24;
32424 dst_s32[i] = x;
32425 }
32426
32427 (void)ditherMode;
32428}
32429
32430static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32431{
32432 ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
32433}
32434
32435#if defined(MA_SUPPORT_SSE2)
32436static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32437{
32438 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
32439}
32440#endif
32441#if defined(MA_SUPPORT_AVX2)
32442static MA_INLINE void ma_pcm_u8_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32443{
32444 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
32445}
32446#endif
32447#if defined(MA_SUPPORT_NEON)
32448static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32449{
32450 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
32451}
32452#endif
32453
32454void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32455{
32456#ifdef MA_USE_REFERENCE_CONVERSION_APIS
32457 ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
32458#else
32459 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
32460 if (ma_has_avx2()) {
32461 ma_pcm_u8_to_s32__avx2(dst, src, count, ditherMode);
32462 } else
32463 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
32464 if (ma_has_sse2()) {
32465 ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode);
32466 } else
32467 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
32468 if (ma_has_neon()) {
32469 ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode);
32470 } else
32471 #endif
32472 {
32473 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
32474 }
32475#endif
32476}
32477
32478
32479static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32480{
32481 float* dst_f32 = (float*)dst;
32482 const ma_uint8* src_u8 = (const ma_uint8*)src;
32483
32484 ma_uint64 i;
32485 for (i = 0; i < count; i += 1) {
32486 float x = (float)src_u8[i];
32487 x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */
32488 x = x - 1; /* 0..2 to -1..1 */
32489
32490 dst_f32[i] = x;
32491 }
32492
32493 (void)ditherMode;
32494}
32495
32496static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32497{
32498 ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
32499}
32500
32501#if defined(MA_SUPPORT_SSE2)
32502static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32503{
32504 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
32505}
32506#endif
32507#if defined(MA_SUPPORT_AVX2)
32508static MA_INLINE void ma_pcm_u8_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32509{
32510 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
32511}
32512#endif
32513#if defined(MA_SUPPORT_NEON)
32514static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32515{
32516 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
32517}
32518#endif
32519
32520void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32521{
32522#ifdef MA_USE_REFERENCE_CONVERSION_APIS
32523 ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
32524#else
32525 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
32526 if (ma_has_avx2()) {
32527 ma_pcm_u8_to_f32__avx2(dst, src, count, ditherMode);
32528 } else
32529 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
32530 if (ma_has_sse2()) {
32531 ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode);
32532 } else
32533 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
32534 if (ma_has_neon()) {
32535 ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode);
32536 } else
32537 #endif
32538 {
32539 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
32540 }
32541#endif
32542}
32543
32544
32545#ifdef MA_USE_REFERENCE_CONVERSION_APIS
32546static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
32547{
32548 ma_uint8* dst_u8 = (ma_uint8*)dst;
32549 const ma_uint8** src_u8 = (const ma_uint8**)src;
32550
32551 ma_uint64 iFrame;
32552 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
32553 ma_uint32 iChannel;
32554 for (iChannel = 0; iChannel < channels; iChannel += 1) {
32555 dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
32556 }
32557 }
32558}
32559#else
32560static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
32561{
32562 ma_uint8* dst_u8 = (ma_uint8*)dst;
32563 const ma_uint8** src_u8 = (const ma_uint8**)src;
32564
32565 if (channels == 1) {
32566 ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8));
32567 } else if (channels == 2) {
32568 ma_uint64 iFrame;
32569 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
32570 dst_u8[iFrame*2 + 0] = src_u8[0][iFrame];
32571 dst_u8[iFrame*2 + 1] = src_u8[1][iFrame];
32572 }
32573 } else {
32574 ma_uint64 iFrame;
32575 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
32576 ma_uint32 iChannel;
32577 for (iChannel = 0; iChannel < channels; iChannel += 1) {
32578 dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
32579 }
32580 }
32581 }
32582}
32583#endif
32584
32585void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
32586{
32587#ifdef MA_USE_REFERENCE_CONVERSION_APIS
32588 ma_pcm_interleave_u8__reference(dst, src, frameCount, channels);
32589#else
32590 ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels);
32591#endif
32592}
32593
32594
32595static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
32596{
32597 ma_uint8** dst_u8 = (ma_uint8**)dst;
32598 const ma_uint8* src_u8 = (const ma_uint8*)src;
32599
32600 ma_uint64 iFrame;
32601 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
32602 ma_uint32 iChannel;
32603 for (iChannel = 0; iChannel < channels; iChannel += 1) {
32604 dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel];
32605 }
32606 }
32607}
32608
32609static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
32610{
32611 ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
32612}
32613
32614void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
32615{
32616#ifdef MA_USE_REFERENCE_CONVERSION_APIS
32617 ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
32618#else
32619 ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels);
32620#endif
32621}
32622
32623
32624/* s16 */
32625static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32626{
32627 ma_uint8* dst_u8 = (ma_uint8*)dst;
32628 const ma_int16* src_s16 = (const ma_int16*)src;
32629
32630 if (ditherMode == ma_dither_mode_none) {
32631 ma_uint64 i;
32632 for (i = 0; i < count; i += 1) {
32633 ma_int16 x = src_s16[i];
32634 x = x >> 8;
32635 x = x + 128;
32636 dst_u8[i] = (ma_uint8)x;
32637 }
32638 } else {
32639 ma_uint64 i;
32640 for (i = 0; i < count; i += 1) {
32641 ma_int16 x = src_s16[i];
32642
32643 /* Dither. Don't overflow. */
32644 ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F);
32645 if ((x + dither) <= 0x7FFF) {
32646 x = (ma_int16)(x + dither);
32647 } else {
32648 x = 0x7FFF;
32649 }
32650
32651 x = x >> 8;
32652 x = x + 128;
32653 dst_u8[i] = (ma_uint8)x;
32654 }
32655 }
32656}
32657
32658static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32659{
32660 ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
32661}
32662
32663#if defined(MA_SUPPORT_SSE2)
32664static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32665{
32666 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
32667}
32668#endif
32669#if defined(MA_SUPPORT_AVX2)
32670static MA_INLINE void ma_pcm_s16_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32671{
32672 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
32673}
32674#endif
32675#if defined(MA_SUPPORT_NEON)
32676static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32677{
32678 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
32679}
32680#endif
32681
32682void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32683{
32684#ifdef MA_USE_REFERENCE_CONVERSION_APIS
32685 ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
32686#else
32687 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
32688 if (ma_has_avx2()) {
32689 ma_pcm_s16_to_u8__avx2(dst, src, count, ditherMode);
32690 } else
32691 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
32692 if (ma_has_sse2()) {
32693 ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode);
32694 } else
32695 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
32696 if (ma_has_neon()) {
32697 ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode);
32698 } else
32699 #endif
32700 {
32701 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
32702 }
32703#endif
32704}
32705
32706
32707void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32708{
32709 (void)ditherMode;
32710 ma_copy_memory_64(dst, src, count * sizeof(ma_int16));
32711}
32712
32713
32714static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32715{
32716 ma_uint8* dst_s24 = (ma_uint8*)dst;
32717 const ma_int16* src_s16 = (const ma_int16*)src;
32718
32719 ma_uint64 i;
32720 for (i = 0; i < count; i += 1) {
32721 dst_s24[i*3+0] = 0;
32722 dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF);
32723 dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8);
32724 }
32725
32726 (void)ditherMode;
32727}
32728
32729static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32730{
32731 ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
32732}
32733
32734#if defined(MA_SUPPORT_SSE2)
32735static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32736{
32737 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
32738}
32739#endif
32740#if defined(MA_SUPPORT_AVX2)
32741static MA_INLINE void ma_pcm_s16_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32742{
32743 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
32744}
32745#endif
32746#if defined(MA_SUPPORT_NEON)
32747static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32748{
32749 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
32750}
32751#endif
32752
32753void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32754{
32755#ifdef MA_USE_REFERENCE_CONVERSION_APIS
32756 ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
32757#else
32758 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
32759 if (ma_has_avx2()) {
32760 ma_pcm_s16_to_s24__avx2(dst, src, count, ditherMode);
32761 } else
32762 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
32763 if (ma_has_sse2()) {
32764 ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode);
32765 } else
32766 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
32767 if (ma_has_neon()) {
32768 ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode);
32769 } else
32770 #endif
32771 {
32772 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
32773 }
32774#endif
32775}
32776
32777
32778static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32779{
32780 ma_int32* dst_s32 = (ma_int32*)dst;
32781 const ma_int16* src_s16 = (const ma_int16*)src;
32782
32783 ma_uint64 i;
32784 for (i = 0; i < count; i += 1) {
32785 dst_s32[i] = src_s16[i] << 16;
32786 }
32787
32788 (void)ditherMode;
32789}
32790
32791static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32792{
32793 ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
32794}
32795
32796#if defined(MA_SUPPORT_SSE2)
32797static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32798{
32799 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
32800}
32801#endif
32802#if defined(MA_SUPPORT_AVX2)
32803static MA_INLINE void ma_pcm_s16_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32804{
32805 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
32806}
32807#endif
32808#if defined(MA_SUPPORT_NEON)
32809static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32810{
32811 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
32812}
32813#endif
32814
32815void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32816{
32817#ifdef MA_USE_REFERENCE_CONVERSION_APIS
32818 ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
32819#else
32820 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
32821 if (ma_has_avx2()) {
32822 ma_pcm_s16_to_s32__avx2(dst, src, count, ditherMode);
32823 } else
32824 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
32825 if (ma_has_sse2()) {
32826 ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode);
32827 } else
32828 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
32829 if (ma_has_neon()) {
32830 ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode);
32831 } else
32832 #endif
32833 {
32834 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
32835 }
32836#endif
32837}
32838
32839
32840static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32841{
32842 float* dst_f32 = (float*)dst;
32843 const ma_int16* src_s16 = (const ma_int16*)src;
32844
32845 ma_uint64 i;
32846 for (i = 0; i < count; i += 1) {
32847 float x = (float)src_s16[i];
32848
32849#if 0
32850 /* The accurate way. */
32851 x = x + 32768.0f; /* -32768..32767 to 0..65535 */
32852 x = x * 0.00003051804379339284f; /* 0..65535 to 0..2 */
32853 x = x - 1; /* 0..2 to -1..1 */
32854#else
32855 /* The fast way. */
32856 x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */
32857#endif
32858
32859 dst_f32[i] = x;
32860 }
32861
32862 (void)ditherMode;
32863}
32864
32865static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32866{
32867 ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
32868}
32869
32870#if defined(MA_SUPPORT_SSE2)
32871static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32872{
32873 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
32874}
32875#endif
32876#if defined(MA_SUPPORT_AVX2)
32877static MA_INLINE void ma_pcm_s16_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32878{
32879 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
32880}
32881#endif
32882#if defined(MA_SUPPORT_NEON)
32883static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32884{
32885 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
32886}
32887#endif
32888
32889void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32890{
32891#ifdef MA_USE_REFERENCE_CONVERSION_APIS
32892 ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
32893#else
32894 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
32895 if (ma_has_avx2()) {
32896 ma_pcm_s16_to_f32__avx2(dst, src, count, ditherMode);
32897 } else
32898 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
32899 if (ma_has_sse2()) {
32900 ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode);
32901 } else
32902 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
32903 if (ma_has_neon()) {
32904 ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode);
32905 } else
32906 #endif
32907 {
32908 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
32909 }
32910#endif
32911}
32912
32913
32914static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
32915{
32916 ma_int16* dst_s16 = (ma_int16*)dst;
32917 const ma_int16** src_s16 = (const ma_int16**)src;
32918
32919 ma_uint64 iFrame;
32920 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
32921 ma_uint32 iChannel;
32922 for (iChannel = 0; iChannel < channels; iChannel += 1) {
32923 dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame];
32924 }
32925 }
32926}
32927
32928static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
32929{
32930 ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
32931}
32932
32933void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
32934{
32935#ifdef MA_USE_REFERENCE_CONVERSION_APIS
32936 ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
32937#else
32938 ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels);
32939#endif
32940}
32941
32942
32943static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
32944{
32945 ma_int16** dst_s16 = (ma_int16**)dst;
32946 const ma_int16* src_s16 = (const ma_int16*)src;
32947
32948 ma_uint64 iFrame;
32949 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
32950 ma_uint32 iChannel;
32951 for (iChannel = 0; iChannel < channels; iChannel += 1) {
32952 dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel];
32953 }
32954 }
32955}
32956
32957static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
32958{
32959 ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
32960}
32961
32962void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
32963{
32964#ifdef MA_USE_REFERENCE_CONVERSION_APIS
32965 ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
32966#else
32967 ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels);
32968#endif
32969}
32970
32971
32972/* s24 */
32973static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
32974{
32975 ma_uint8* dst_u8 = (ma_uint8*)dst;
32976 const ma_uint8* src_s24 = (const ma_uint8*)src;
32977
32978 if (ditherMode == ma_dither_mode_none) {
32979 ma_uint64 i;
32980 for (i = 0; i < count; i += 1) {
32981 ma_int8 x = (ma_int8)src_s24[i*3 + 2] + 128;
32982 dst_u8[i] = (ma_uint8)x;
32983 }
32984 } else {
32985 ma_uint64 i;
32986 for (i = 0; i < count; i += 1) {
32987 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);
32988
32989 /* Dither. Don't overflow. */
32990 ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
32991 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
32992 x = x + dither;
32993 } else {
32994 x = 0x7FFFFFFF;
32995 }
32996
32997 x = x >> 24;
32998 x = x + 128;
32999 dst_u8[i] = (ma_uint8)x;
33000 }
33001 }
33002}
33003
33004static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33005{
33006 ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
33007}
33008
33009#if defined(MA_SUPPORT_SSE2)
33010static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33011{
33012 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
33013}
33014#endif
33015#if defined(MA_SUPPORT_AVX2)
33016static MA_INLINE void ma_pcm_s24_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33017{
33018 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
33019}
33020#endif
33021#if defined(MA_SUPPORT_NEON)
33022static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33023{
33024 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
33025}
33026#endif
33027
33028void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33029{
33030#ifdef MA_USE_REFERENCE_CONVERSION_APIS
33031 ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
33032#else
33033 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
33034 if (ma_has_avx2()) {
33035 ma_pcm_s24_to_u8__avx2(dst, src, count, ditherMode);
33036 } else
33037 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
33038 if (ma_has_sse2()) {
33039 ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode);
33040 } else
33041 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
33042 if (ma_has_neon()) {
33043 ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode);
33044 } else
33045 #endif
33046 {
33047 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
33048 }
33049#endif
33050}
33051
33052
33053static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33054{
33055 ma_int16* dst_s16 = (ma_int16*)dst;
33056 const ma_uint8* src_s24 = (const ma_uint8*)src;
33057
33058 if (ditherMode == ma_dither_mode_none) {
33059 ma_uint64 i;
33060 for (i = 0; i < count; i += 1) {
33061 ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]);
33062 ma_uint16 dst_hi = ((ma_uint16)src_s24[i*3 + 2]) << 8;
33063 dst_s16[i] = (ma_int16)dst_lo | dst_hi;
33064 }
33065 } else {
33066 ma_uint64 i;
33067 for (i = 0; i < count; i += 1) {
33068 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);
33069
33070 /* Dither. Don't overflow. */
33071 ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
33072 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
33073 x = x + dither;
33074 } else {
33075 x = 0x7FFFFFFF;
33076 }
33077
33078 x = x >> 16;
33079 dst_s16[i] = (ma_int16)x;
33080 }
33081 }
33082}
33083
33084static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33085{
33086 ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
33087}
33088
33089#if defined(MA_SUPPORT_SSE2)
33090static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33091{
33092 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
33093}
33094#endif
33095#if defined(MA_SUPPORT_AVX2)
33096static MA_INLINE void ma_pcm_s24_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33097{
33098 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
33099}
33100#endif
33101#if defined(MA_SUPPORT_NEON)
33102static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33103{
33104 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
33105}
33106#endif
33107
33108void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33109{
33110#ifdef MA_USE_REFERENCE_CONVERSION_APIS
33111 ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
33112#else
33113 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
33114 if (ma_has_avx2()) {
33115 ma_pcm_s24_to_s16__avx2(dst, src, count, ditherMode);
33116 } else
33117 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
33118 if (ma_has_sse2()) {
33119 ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode);
33120 } else
33121 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
33122 if (ma_has_neon()) {
33123 ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode);
33124 } else
33125 #endif
33126 {
33127 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
33128 }
33129#endif
33130}
33131
33132
33133void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33134{
33135 (void)ditherMode;
33136
33137 ma_copy_memory_64(dst, src, count * 3);
33138}
33139
33140
33141static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33142{
33143 ma_int32* dst_s32 = (ma_int32*)dst;
33144 const ma_uint8* src_s24 = (const ma_uint8*)src;
33145
33146 ma_uint64 i;
33147 for (i = 0; i < count; i += 1) {
33148 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);
33149 }
33150
33151 (void)ditherMode;
33152}
33153
33154static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33155{
33156 ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
33157}
33158
33159#if defined(MA_SUPPORT_SSE2)
33160static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33161{
33162 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
33163}
33164#endif
33165#if defined(MA_SUPPORT_AVX2)
33166static MA_INLINE void ma_pcm_s24_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33167{
33168 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
33169}
33170#endif
33171#if defined(MA_SUPPORT_NEON)
33172static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33173{
33174 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
33175}
33176#endif
33177
33178void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33179{
33180#ifdef MA_USE_REFERENCE_CONVERSION_APIS
33181 ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
33182#else
33183 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
33184 if (ma_has_avx2()) {
33185 ma_pcm_s24_to_s32__avx2(dst, src, count, ditherMode);
33186 } else
33187 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
33188 if (ma_has_sse2()) {
33189 ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode);
33190 } else
33191 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
33192 if (ma_has_neon()) {
33193 ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode);
33194 } else
33195 #endif
33196 {
33197 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
33198 }
33199#endif
33200}
33201
33202
33203static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33204{
33205 float* dst_f32 = (float*)dst;
33206 const ma_uint8* src_s24 = (const ma_uint8*)src;
33207
33208 ma_uint64 i;
33209 for (i = 0; i < count; i += 1) {
33210 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);
33211
33212#if 0
33213 /* The accurate way. */
33214 x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */
33215 x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */
33216 x = x - 1; /* 0..2 to -1..1 */
33217#else
33218 /* The fast way. */
33219 x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */
33220#endif
33221
33222 dst_f32[i] = x;
33223 }
33224
33225 (void)ditherMode;
33226}
33227
33228static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33229{
33230 ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
33231}
33232
33233#if defined(MA_SUPPORT_SSE2)
33234static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33235{
33236 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
33237}
33238#endif
33239#if defined(MA_SUPPORT_AVX2)
33240static MA_INLINE void ma_pcm_s24_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33241{
33242 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
33243}
33244#endif
33245#if defined(MA_SUPPORT_NEON)
33246static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33247{
33248 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
33249}
33250#endif
33251
33252void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33253{
33254#ifdef MA_USE_REFERENCE_CONVERSION_APIS
33255 ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
33256#else
33257 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
33258 if (ma_has_avx2()) {
33259 ma_pcm_s24_to_f32__avx2(dst, src, count, ditherMode);
33260 } else
33261 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
33262 if (ma_has_sse2()) {
33263 ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode);
33264 } else
33265 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
33266 if (ma_has_neon()) {
33267 ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode);
33268 } else
33269 #endif
33270 {
33271 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
33272 }
33273#endif
33274}
33275
33276
33277static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
33278{
33279 ma_uint8* dst8 = (ma_uint8*)dst;
33280 const ma_uint8** src8 = (const ma_uint8**)src;
33281
33282 ma_uint64 iFrame;
33283 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
33284 ma_uint32 iChannel;
33285 for (iChannel = 0; iChannel < channels; iChannel += 1) {
33286 dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0];
33287 dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1];
33288 dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2];
33289 }
33290 }
33291}
33292
33293static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
33294{
33295 ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
33296}
33297
33298void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
33299{
33300#ifdef MA_USE_REFERENCE_CONVERSION_APIS
33301 ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
33302#else
33303 ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels);
33304#endif
33305}
33306
33307
33308static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
33309{
33310 ma_uint8** dst8 = (ma_uint8**)dst;
33311 const ma_uint8* src8 = (const ma_uint8*)src;
33312
33313 ma_uint32 iFrame;
33314 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
33315 ma_uint32 iChannel;
33316 for (iChannel = 0; iChannel < channels; iChannel += 1) {
33317 dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0];
33318 dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1];
33319 dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2];
33320 }
33321 }
33322}
33323
33324static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
33325{
33326 ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
33327}
33328
33329void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
33330{
33331#ifdef MA_USE_REFERENCE_CONVERSION_APIS
33332 ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
33333#else
33334 ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels);
33335#endif
33336}
33337
33338
33339
33340/* s32 */
33341static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33342{
33343 ma_uint8* dst_u8 = (ma_uint8*)dst;
33344 const ma_int32* src_s32 = (const ma_int32*)src;
33345
33346 if (ditherMode == ma_dither_mode_none) {
33347 ma_uint64 i;
33348 for (i = 0; i < count; i += 1) {
33349 ma_int32 x = src_s32[i];
33350 x = x >> 24;
33351 x = x + 128;
33352 dst_u8[i] = (ma_uint8)x;
33353 }
33354 } else {
33355 ma_uint64 i;
33356 for (i = 0; i < count; i += 1) {
33357 ma_int32 x = src_s32[i];
33358
33359 /* Dither. Don't overflow. */
33360 ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
33361 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
33362 x = x + dither;
33363 } else {
33364 x = 0x7FFFFFFF;
33365 }
33366
33367 x = x >> 24;
33368 x = x + 128;
33369 dst_u8[i] = (ma_uint8)x;
33370 }
33371 }
33372}
33373
33374static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33375{
33376 ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
33377}
33378
33379#if defined(MA_SUPPORT_SSE2)
33380static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33381{
33382 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
33383}
33384#endif
33385#if defined(MA_SUPPORT_AVX2)
33386static MA_INLINE void ma_pcm_s32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33387{
33388 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
33389}
33390#endif
33391#if defined(MA_SUPPORT_NEON)
33392static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33393{
33394 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
33395}
33396#endif
33397
33398void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33399{
33400#ifdef MA_USE_REFERENCE_CONVERSION_APIS
33401 ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
33402#else
33403 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
33404 if (ma_has_avx2()) {
33405 ma_pcm_s32_to_u8__avx2(dst, src, count, ditherMode);
33406 } else
33407 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
33408 if (ma_has_sse2()) {
33409 ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode);
33410 } else
33411 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
33412 if (ma_has_neon()) {
33413 ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode);
33414 } else
33415 #endif
33416 {
33417 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
33418 }
33419#endif
33420}
33421
33422
33423static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33424{
33425 ma_int16* dst_s16 = (ma_int16*)dst;
33426 const ma_int32* src_s32 = (const ma_int32*)src;
33427
33428 if (ditherMode == ma_dither_mode_none) {
33429 ma_uint64 i;
33430 for (i = 0; i < count; i += 1) {
33431 ma_int32 x = src_s32[i];
33432 x = x >> 16;
33433 dst_s16[i] = (ma_int16)x;
33434 }
33435 } else {
33436 ma_uint64 i;
33437 for (i = 0; i < count; i += 1) {
33438 ma_int32 x = src_s32[i];
33439
33440 /* Dither. Don't overflow. */
33441 ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
33442 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
33443 x = x + dither;
33444 } else {
33445 x = 0x7FFFFFFF;
33446 }
33447
33448 x = x >> 16;
33449 dst_s16[i] = (ma_int16)x;
33450 }
33451 }
33452}
33453
33454static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33455{
33456 ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
33457}
33458
33459#if defined(MA_SUPPORT_SSE2)
33460static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33461{
33462 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
33463}
33464#endif
33465#if defined(MA_SUPPORT_AVX2)
33466static MA_INLINE void ma_pcm_s32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33467{
33468 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
33469}
33470#endif
33471#if defined(MA_SUPPORT_NEON)
33472static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33473{
33474 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
33475}
33476#endif
33477
33478void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33479{
33480#ifdef MA_USE_REFERENCE_CONVERSION_APIS
33481 ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
33482#else
33483 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
33484 if (ma_has_avx2()) {
33485 ma_pcm_s32_to_s16__avx2(dst, src, count, ditherMode);
33486 } else
33487 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
33488 if (ma_has_sse2()) {
33489 ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode);
33490 } else
33491 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
33492 if (ma_has_neon()) {
33493 ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode);
33494 } else
33495 #endif
33496 {
33497 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
33498 }
33499#endif
33500}
33501
33502
33503static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33504{
33505 ma_uint8* dst_s24 = (ma_uint8*)dst;
33506 const ma_int32* src_s32 = (const ma_int32*)src;
33507
33508 ma_uint64 i;
33509 for (i = 0; i < count; i += 1) {
33510 ma_uint32 x = (ma_uint32)src_s32[i];
33511 dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8);
33512 dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16);
33513 dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24);
33514 }
33515
33516 (void)ditherMode; /* No dithering for s32 -> s24. */
33517}
33518
33519static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33520{
33521 ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
33522}
33523
33524#if defined(MA_SUPPORT_SSE2)
33525static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33526{
33527 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
33528}
33529#endif
33530#if defined(MA_SUPPORT_AVX2)
33531static MA_INLINE void ma_pcm_s32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33532{
33533 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
33534}
33535#endif
33536#if defined(MA_SUPPORT_NEON)
33537static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33538{
33539 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
33540}
33541#endif
33542
33543void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33544{
33545#ifdef MA_USE_REFERENCE_CONVERSION_APIS
33546 ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
33547#else
33548 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
33549 if (ma_has_avx2()) {
33550 ma_pcm_s32_to_s24__avx2(dst, src, count, ditherMode);
33551 } else
33552 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
33553 if (ma_has_sse2()) {
33554 ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode);
33555 } else
33556 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
33557 if (ma_has_neon()) {
33558 ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode);
33559 } else
33560 #endif
33561 {
33562 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
33563 }
33564#endif
33565}
33566
33567
33568void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33569{
33570 (void)ditherMode;
33571
33572 ma_copy_memory_64(dst, src, count * sizeof(ma_int32));
33573}
33574
33575
33576static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33577{
33578 float* dst_f32 = (float*)dst;
33579 const ma_int32* src_s32 = (const ma_int32*)src;
33580
33581 ma_uint64 i;
33582 for (i = 0; i < count; i += 1) {
33583 double x = src_s32[i];
33584
33585#if 0
33586 x = x + 2147483648.0;
33587 x = x * 0.0000000004656612873077392578125;
33588 x = x - 1;
33589#else
33590 x = x / 2147483648.0;
33591#endif
33592
33593 dst_f32[i] = (float)x;
33594 }
33595
33596 (void)ditherMode; /* No dithering for s32 -> f32. */
33597}
33598
33599static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33600{
33601 ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
33602}
33603
33604#if defined(MA_SUPPORT_SSE2)
33605static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33606{
33607 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
33608}
33609#endif
33610#if defined(MA_SUPPORT_AVX2)
33611static MA_INLINE void ma_pcm_s32_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33612{
33613 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
33614}
33615#endif
33616#if defined(MA_SUPPORT_NEON)
33617static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33618{
33619 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
33620}
33621#endif
33622
33623void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33624{
33625#ifdef MA_USE_REFERENCE_CONVERSION_APIS
33626 ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
33627#else
33628 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
33629 if (ma_has_avx2()) {
33630 ma_pcm_s32_to_f32__avx2(dst, src, count, ditherMode);
33631 } else
33632 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
33633 if (ma_has_sse2()) {
33634 ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode);
33635 } else
33636 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
33637 if (ma_has_neon()) {
33638 ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode);
33639 } else
33640 #endif
33641 {
33642 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
33643 }
33644#endif
33645}
33646
33647
33648static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
33649{
33650 ma_int32* dst_s32 = (ma_int32*)dst;
33651 const ma_int32** src_s32 = (const ma_int32**)src;
33652
33653 ma_uint64 iFrame;
33654 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
33655 ma_uint32 iChannel;
33656 for (iChannel = 0; iChannel < channels; iChannel += 1) {
33657 dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame];
33658 }
33659 }
33660}
33661
33662static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
33663{
33664 ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
33665}
33666
33667void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
33668{
33669#ifdef MA_USE_REFERENCE_CONVERSION_APIS
33670 ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
33671#else
33672 ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels);
33673#endif
33674}
33675
33676
33677static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
33678{
33679 ma_int32** dst_s32 = (ma_int32**)dst;
33680 const ma_int32* src_s32 = (const ma_int32*)src;
33681
33682 ma_uint64 iFrame;
33683 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
33684 ma_uint32 iChannel;
33685 for (iChannel = 0; iChannel < channels; iChannel += 1) {
33686 dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel];
33687 }
33688 }
33689}
33690
33691static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
33692{
33693 ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
33694}
33695
33696void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
33697{
33698#ifdef MA_USE_REFERENCE_CONVERSION_APIS
33699 ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
33700#else
33701 ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels);
33702#endif
33703}
33704
33705
33706/* f32 */
33707static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33708{
33709 ma_uint64 i;
33710
33711 ma_uint8* dst_u8 = (ma_uint8*)dst;
33712 const float* src_f32 = (const float*)src;
33713
33714 float ditherMin = 0;
33715 float ditherMax = 0;
33716 if (ditherMode != ma_dither_mode_none) {
33717 ditherMin = 1.0f / -128;
33718 ditherMax = 1.0f / 127;
33719 }
33720
33721 for (i = 0; i < count; i += 1) {
33722 float x = src_f32[i];
33723 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
33724 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
33725 x = x + 1; /* -1..1 to 0..2 */
33726 x = x * 127.5f; /* 0..2 to 0..255 */
33727
33728 dst_u8[i] = (ma_uint8)x;
33729 }
33730}
33731
33732static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33733{
33734 ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
33735}
33736
33737#if defined(MA_SUPPORT_SSE2)
33738static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33739{
33740 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
33741}
33742#endif
33743#if defined(MA_SUPPORT_AVX2)
33744static MA_INLINE void ma_pcm_f32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33745{
33746 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
33747}
33748#endif
33749#if defined(MA_SUPPORT_NEON)
33750static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33751{
33752 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
33753}
33754#endif
33755
33756void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33757{
33758#ifdef MA_USE_REFERENCE_CONVERSION_APIS
33759 ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
33760#else
33761 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
33762 if (ma_has_avx2()) {
33763 ma_pcm_f32_to_u8__avx2(dst, src, count, ditherMode);
33764 } else
33765 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
33766 if (ma_has_sse2()) {
33767 ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode);
33768 } else
33769 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
33770 if (ma_has_neon()) {
33771 ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode);
33772 } else
33773 #endif
33774 {
33775 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
33776 }
33777#endif
33778}
33779
33780#ifdef MA_USE_REFERENCE_CONVERSION_APIS
33781static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33782{
33783 ma_uint64 i;
33784
33785 ma_int16* dst_s16 = (ma_int16*)dst;
33786 const float* src_f32 = (const float*)src;
33787
33788 float ditherMin = 0;
33789 float ditherMax = 0;
33790 if (ditherMode != ma_dither_mode_none) {
33791 ditherMin = 1.0f / -32768;
33792 ditherMax = 1.0f / 32767;
33793 }
33794
33795 for (i = 0; i < count; i += 1) {
33796 float x = src_f32[i];
33797 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
33798 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
33799
33800#if 0
33801 /* The accurate way. */
33802 x = x + 1; /* -1..1 to 0..2 */
33803 x = x * 32767.5f; /* 0..2 to 0..65535 */
33804 x = x - 32768.0f; /* 0...65535 to -32768..32767 */
33805#else
33806 /* The fast way. */
33807 x = x * 32767.0f; /* -1..1 to -32767..32767 */
33808#endif
33809
33810 dst_s16[i] = (ma_int16)x;
33811 }
33812}
33813#else
33814static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33815{
33816 ma_uint64 i;
33817 ma_uint64 i4;
33818 ma_uint64 count4;
33819
33820 ma_int16* dst_s16 = (ma_int16*)dst;
33821 const float* src_f32 = (const float*)src;
33822
33823 float ditherMin = 0;
33824 float ditherMax = 0;
33825 if (ditherMode != ma_dither_mode_none) {
33826 ditherMin = 1.0f / -32768;
33827 ditherMax = 1.0f / 32767;
33828 }
33829
33830 /* Unrolled. */
33831 i = 0;
33832 count4 = count >> 2;
33833 for (i4 = 0; i4 < count4; i4 += 1) {
33834 float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
33835 float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
33836 float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
33837 float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
33838
33839 float x0 = src_f32[i+0];
33840 float x1 = src_f32[i+1];
33841 float x2 = src_f32[i+2];
33842 float x3 = src_f32[i+3];
33843
33844 x0 = x0 + d0;
33845 x1 = x1 + d1;
33846 x2 = x2 + d2;
33847 x3 = x3 + d3;
33848
33849 x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
33850 x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
33851 x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
33852 x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
33853
33854 x0 = x0 * 32767.0f;
33855 x1 = x1 * 32767.0f;
33856 x2 = x2 * 32767.0f;
33857 x3 = x3 * 32767.0f;
33858
33859 dst_s16[i+0] = (ma_int16)x0;
33860 dst_s16[i+1] = (ma_int16)x1;
33861 dst_s16[i+2] = (ma_int16)x2;
33862 dst_s16[i+3] = (ma_int16)x3;
33863
33864 i += 4;
33865 }
33866
33867 /* Leftover. */
33868 for (; i < count; i += 1) {
33869 float x = src_f32[i];
33870 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
33871 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
33872 x = x * 32767.0f; /* -1..1 to -32767..32767 */
33873
33874 dst_s16[i] = (ma_int16)x;
33875 }
33876}
33877#endif
33878
33879#if defined(MA_SUPPORT_SSE2)
33880static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33881{
33882 ma_uint64 i;
33883 ma_uint64 i8;
33884 ma_uint64 count8;
33885 ma_int16* dst_s16;
33886 const float* src_f32;
33887 float ditherMin;
33888 float ditherMax;
33889
33890 /* Both the input and output buffers need to be aligned to 16 bytes. */
33891 if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
33892 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
33893 return;
33894 }
33895
33896 dst_s16 = (ma_int16*)dst;
33897 src_f32 = (const float*)src;
33898
33899 ditherMin = 0;
33900 ditherMax = 0;
33901 if (ditherMode != ma_dither_mode_none) {
33902 ditherMin = 1.0f / -32768;
33903 ditherMax = 1.0f / 32767;
33904 }
33905
33906 i = 0;
33907
33908 /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
33909 count8 = count >> 3;
33910 for (i8 = 0; i8 < count8; i8 += 1) {
33911 __m128 d0;
33912 __m128 d1;
33913 __m128 x0;
33914 __m128 x1;
33915
33916 if (ditherMode == ma_dither_mode_none) {
33917 d0 = _mm_set1_ps(0);
33918 d1 = _mm_set1_ps(0);
33919 } else if (ditherMode == ma_dither_mode_rectangle) {
33920 d0 = _mm_set_ps(
33921 ma_dither_f32_rectangle(ditherMin, ditherMax),
33922 ma_dither_f32_rectangle(ditherMin, ditherMax),
33923 ma_dither_f32_rectangle(ditherMin, ditherMax),
33924 ma_dither_f32_rectangle(ditherMin, ditherMax)
33925 );
33926 d1 = _mm_set_ps(
33927 ma_dither_f32_rectangle(ditherMin, ditherMax),
33928 ma_dither_f32_rectangle(ditherMin, ditherMax),
33929 ma_dither_f32_rectangle(ditherMin, ditherMax),
33930 ma_dither_f32_rectangle(ditherMin, ditherMax)
33931 );
33932 } else {
33933 d0 = _mm_set_ps(
33934 ma_dither_f32_triangle(ditherMin, ditherMax),
33935 ma_dither_f32_triangle(ditherMin, ditherMax),
33936 ma_dither_f32_triangle(ditherMin, ditherMax),
33937 ma_dither_f32_triangle(ditherMin, ditherMax)
33938 );
33939 d1 = _mm_set_ps(
33940 ma_dither_f32_triangle(ditherMin, ditherMax),
33941 ma_dither_f32_triangle(ditherMin, ditherMax),
33942 ma_dither_f32_triangle(ditherMin, ditherMax),
33943 ma_dither_f32_triangle(ditherMin, ditherMax)
33944 );
33945 }
33946
33947 x0 = *((__m128*)(src_f32 + i) + 0);
33948 x1 = *((__m128*)(src_f32 + i) + 1);
33949
33950 x0 = _mm_add_ps(x0, d0);
33951 x1 = _mm_add_ps(x1, d1);
33952
33953 x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f));
33954 x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f));
33955
33956 _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1)));
33957
33958 i += 8;
33959 }
33960
33961
33962 /* Leftover. */
33963 for (; i < count; i += 1) {
33964 float x = src_f32[i];
33965 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
33966 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
33967 x = x * 32767.0f; /* -1..1 to -32767..32767 */
33968
33969 dst_s16[i] = (ma_int16)x;
33970 }
33971}
33972#endif
33973#if defined(MA_SUPPORT_AVX2)
33974static MA_INLINE void ma_pcm_f32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
33975{
33976 ma_uint64 i;
33977 ma_uint64 i16;
33978 ma_uint64 count16;
33979 ma_int16* dst_s16;
33980 const float* src_f32;
33981 float ditherMin;
33982 float ditherMax;
33983
33984 /* Both the input and output buffers need to be aligned to 32 bytes. */
33985 if ((((ma_uintptr)dst & 31) != 0) || (((ma_uintptr)src & 31) != 0)) {
33986 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
33987 return;
33988 }
33989
33990 dst_s16 = (ma_int16*)dst;
33991 src_f32 = (const float*)src;
33992
33993 ditherMin = 0;
33994 ditherMax = 0;
33995 if (ditherMode != ma_dither_mode_none) {
33996 ditherMin = 1.0f / -32768;
33997 ditherMax = 1.0f / 32767;
33998 }
33999
34000 i = 0;
34001
34002 /* AVX2. AVX2 allows us to output 16 s16's at a time which means our loop is unrolled 16 times. */
34003 count16 = count >> 4;
34004 for (i16 = 0; i16 < count16; i16 += 1) {
34005 __m256 d0;
34006 __m256 d1;
34007 __m256 x0;
34008 __m256 x1;
34009 __m256i i0;
34010 __m256i i1;
34011 __m256i p0;
34012 __m256i p1;
34013 __m256i r;
34014
34015 if (ditherMode == ma_dither_mode_none) {
34016 d0 = _mm256_set1_ps(0);
34017 d1 = _mm256_set1_ps(0);
34018 } else if (ditherMode == ma_dither_mode_rectangle) {
34019 d0 = _mm256_set_ps(
34020 ma_dither_f32_rectangle(ditherMin, ditherMax),
34021 ma_dither_f32_rectangle(ditherMin, ditherMax),
34022 ma_dither_f32_rectangle(ditherMin, ditherMax),
34023 ma_dither_f32_rectangle(ditherMin, ditherMax),
34024 ma_dither_f32_rectangle(ditherMin, ditherMax),
34025 ma_dither_f32_rectangle(ditherMin, ditherMax),
34026 ma_dither_f32_rectangle(ditherMin, ditherMax),
34027 ma_dither_f32_rectangle(ditherMin, ditherMax)
34028 );
34029 d1 = _mm256_set_ps(
34030 ma_dither_f32_rectangle(ditherMin, ditherMax),
34031 ma_dither_f32_rectangle(ditherMin, ditherMax),
34032 ma_dither_f32_rectangle(ditherMin, ditherMax),
34033 ma_dither_f32_rectangle(ditherMin, ditherMax),
34034 ma_dither_f32_rectangle(ditherMin, ditherMax),
34035 ma_dither_f32_rectangle(ditherMin, ditherMax),
34036 ma_dither_f32_rectangle(ditherMin, ditherMax),
34037 ma_dither_f32_rectangle(ditherMin, ditherMax)
34038 );
34039 } else {
34040 d0 = _mm256_set_ps(
34041 ma_dither_f32_triangle(ditherMin, ditherMax),
34042 ma_dither_f32_triangle(ditherMin, ditherMax),
34043 ma_dither_f32_triangle(ditherMin, ditherMax),
34044 ma_dither_f32_triangle(ditherMin, ditherMax),
34045 ma_dither_f32_triangle(ditherMin, ditherMax),
34046 ma_dither_f32_triangle(ditherMin, ditherMax),
34047 ma_dither_f32_triangle(ditherMin, ditherMax),
34048 ma_dither_f32_triangle(ditherMin, ditherMax)
34049 );
34050 d1 = _mm256_set_ps(
34051 ma_dither_f32_triangle(ditherMin, ditherMax),
34052 ma_dither_f32_triangle(ditherMin, ditherMax),
34053 ma_dither_f32_triangle(ditherMin, ditherMax),
34054 ma_dither_f32_triangle(ditherMin, ditherMax),
34055 ma_dither_f32_triangle(ditherMin, ditherMax),
34056 ma_dither_f32_triangle(ditherMin, ditherMax),
34057 ma_dither_f32_triangle(ditherMin, ditherMax),
34058 ma_dither_f32_triangle(ditherMin, ditherMax)
34059 );
34060 }
34061
34062 x0 = *((__m256*)(src_f32 + i) + 0);
34063 x1 = *((__m256*)(src_f32 + i) + 1);
34064
34065 x0 = _mm256_add_ps(x0, d0);
34066 x1 = _mm256_add_ps(x1, d1);
34067
34068 x0 = _mm256_mul_ps(x0, _mm256_set1_ps(32767.0f));
34069 x1 = _mm256_mul_ps(x1, _mm256_set1_ps(32767.0f));
34070
34071 /* Computing the final result is a little more complicated for AVX2 than SSE2. */
34072 i0 = _mm256_cvttps_epi32(x0);
34073 i1 = _mm256_cvttps_epi32(x1);
34074 p0 = _mm256_permute2x128_si256(i0, i1, 0 | 32);
34075 p1 = _mm256_permute2x128_si256(i0, i1, 1 | 48);
34076 r = _mm256_packs_epi32(p0, p1);
34077
34078 _mm256_stream_si256(((__m256i*)(dst_s16 + i)), r);
34079
34080 i += 16;
34081 }
34082
34083
34084 /* Leftover. */
34085 for (; i < count; i += 1) {
34086 float x = src_f32[i];
34087 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
34088 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
34089 x = x * 32767.0f; /* -1..1 to -32767..32767 */
34090
34091 dst_s16[i] = (ma_int16)x;
34092 }
34093}
34094#endif
34095#if defined(MA_SUPPORT_NEON)
34096static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34097{
34098 ma_uint64 i;
34099 ma_uint64 i8;
34100 ma_uint64 count8;
34101 ma_int16* dst_s16;
34102 const float* src_f32;
34103 float ditherMin;
34104 float ditherMax;
34105
34106 if (!ma_has_neon()) {
34107 return ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
34108 }
34109
34110 /* Both the input and output buffers need to be aligned to 16 bytes. */
34111 if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
34112 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
34113 return;
34114 }
34115
34116 dst_s16 = (ma_int16*)dst;
34117 src_f32 = (const float*)src;
34118
34119 ditherMin = 0;
34120 ditherMax = 0;
34121 if (ditherMode != ma_dither_mode_none) {
34122 ditherMin = 1.0f / -32768;
34123 ditherMax = 1.0f / 32767;
34124 }
34125
34126 i = 0;
34127
34128 /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
34129 count8 = count >> 3;
34130 for (i8 = 0; i8 < count8; i8 += 1) {
34131 float32x4_t d0;
34132 float32x4_t d1;
34133 float32x4_t x0;
34134 float32x4_t x1;
34135 int32x4_t i0;
34136 int32x4_t i1;
34137
34138 if (ditherMode == ma_dither_mode_none) {
34139 d0 = vmovq_n_f32(0);
34140 d1 = vmovq_n_f32(0);
34141 } else if (ditherMode == ma_dither_mode_rectangle) {
34142 float d0v[4];
34143 d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
34144 d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
34145 d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
34146 d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
34147 d0 = vld1q_f32(d0v);
34148
34149 float d1v[4];
34150 d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
34151 d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
34152 d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
34153 d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
34154 d1 = vld1q_f32(d1v);
34155 } else {
34156 float d0v[4];
34157 d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
34158 d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
34159 d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
34160 d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
34161 d0 = vld1q_f32(d0v);
34162
34163 float d1v[4];
34164 d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
34165 d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
34166 d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
34167 d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
34168 d1 = vld1q_f32(d1v);
34169 }
34170
34171 x0 = *((float32x4_t*)(src_f32 + i) + 0);
34172 x1 = *((float32x4_t*)(src_f32 + i) + 1);
34173
34174 x0 = vaddq_f32(x0, d0);
34175 x1 = vaddq_f32(x1, d1);
34176
34177 x0 = vmulq_n_f32(x0, 32767.0f);
34178 x1 = vmulq_n_f32(x1, 32767.0f);
34179
34180 i0 = vcvtq_s32_f32(x0);
34181 i1 = vcvtq_s32_f32(x1);
34182 *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1));
34183
34184 i += 8;
34185 }
34186
34187
34188 /* Leftover. */
34189 for (; i < count; i += 1) {
34190 float x = src_f32[i];
34191 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
34192 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
34193 x = x * 32767.0f; /* -1..1 to -32767..32767 */
34194
34195 dst_s16[i] = (ma_int16)x;
34196 }
34197}
34198#endif
34199
34200void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34201{
34202#ifdef MA_USE_REFERENCE_CONVERSION_APIS
34203 ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode);
34204#else
34205 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
34206 if (ma_has_avx2()) {
34207 ma_pcm_f32_to_s16__avx2(dst, src, count, ditherMode);
34208 } else
34209 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
34210 if (ma_has_sse2()) {
34211 ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode);
34212 } else
34213 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34214 if (ma_has_neon()) {
34215 ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode);
34216 } else
34217 #endif
34218 {
34219 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
34220 }
34221#endif
34222}
34223
34224
34225static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34226{
34227 ma_uint8* dst_s24 = (ma_uint8*)dst;
34228 const float* src_f32 = (const float*)src;
34229
34230 ma_uint64 i;
34231 for (i = 0; i < count; i += 1) {
34232 ma_int32 r;
34233 float x = src_f32[i];
34234 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
34235
34236#if 0
34237 /* The accurate way. */
34238 x = x + 1; /* -1..1 to 0..2 */
34239 x = x * 8388607.5f; /* 0..2 to 0..16777215 */
34240 x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */
34241#else
34242 /* The fast way. */
34243 x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */
34244#endif
34245
34246 r = (ma_int32)x;
34247 dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0);
34248 dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8);
34249 dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16);
34250 }
34251
34252 (void)ditherMode; /* No dithering for f32 -> s24. */
34253}
34254
34255static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34256{
34257 ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
34258}
34259
34260#if defined(MA_SUPPORT_SSE2)
34261static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34262{
34263 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
34264}
34265#endif
34266#if defined(MA_SUPPORT_AVX2)
34267static MA_INLINE void ma_pcm_f32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34268{
34269 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
34270}
34271#endif
34272#if defined(MA_SUPPORT_NEON)
34273static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34274{
34275 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
34276}
34277#endif
34278
34279void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34280{
34281#ifdef MA_USE_REFERENCE_CONVERSION_APIS
34282 ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
34283#else
34284 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
34285 if (ma_has_avx2()) {
34286 ma_pcm_f32_to_s24__avx2(dst, src, count, ditherMode);
34287 } else
34288 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
34289 if (ma_has_sse2()) {
34290 ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode);
34291 } else
34292 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34293 if (ma_has_neon()) {
34294 ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode);
34295 } else
34296 #endif
34297 {
34298 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
34299 }
34300#endif
34301}
34302
34303
34304static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34305{
34306 ma_int32* dst_s32 = (ma_int32*)dst;
34307 const float* src_f32 = (const float*)src;
34308
34309 ma_uint32 i;
34310 for (i = 0; i < count; i += 1) {
34311 double x = src_f32[i];
34312 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
34313
34314#if 0
34315 /* The accurate way. */
34316 x = x + 1; /* -1..1 to 0..2 */
34317 x = x * 2147483647.5; /* 0..2 to 0..4294967295 */
34318 x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */
34319#else
34320 /* The fast way. */
34321 x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */
34322#endif
34323
34324 dst_s32[i] = (ma_int32)x;
34325 }
34326
34327 (void)ditherMode; /* No dithering for f32 -> s32. */
34328}
34329
34330static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34331{
34332 ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
34333}
34334
34335#if defined(MA_SUPPORT_SSE2)
34336static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34337{
34338 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
34339}
34340#endif
34341#if defined(MA_SUPPORT_AVX2)
34342static MA_INLINE void ma_pcm_f32_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34343{
34344 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
34345}
34346#endif
34347#if defined(MA_SUPPORT_NEON)
34348static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34349{
34350 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
34351}
34352#endif
34353
34354void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34355{
34356#ifdef MA_USE_REFERENCE_CONVERSION_APIS
34357 ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
34358#else
34359 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
34360 if (ma_has_avx2()) {
34361 ma_pcm_f32_to_s32__avx2(dst, src, count, ditherMode);
34362 } else
34363 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
34364 if (ma_has_sse2()) {
34365 ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode);
34366 } else
34367 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34368 if (ma_has_neon()) {
34369 ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode);
34370 } else
34371 #endif
34372 {
34373 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
34374 }
34375#endif
34376}
34377
34378
34379void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34380{
34381 (void)ditherMode;
34382
34383 ma_copy_memory_64(dst, src, count * sizeof(float));
34384}
34385
34386
34387static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
34388{
34389 float* dst_f32 = (float*)dst;
34390 const float** src_f32 = (const float**)src;
34391
34392 ma_uint64 iFrame;
34393 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34394 ma_uint32 iChannel;
34395 for (iChannel = 0; iChannel < channels; iChannel += 1) {
34396 dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame];
34397 }
34398 }
34399}
34400
34401static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
34402{
34403 ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
34404}
34405
34406void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
34407{
34408#ifdef MA_USE_REFERENCE_CONVERSION_APIS
34409 ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
34410#else
34411 ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels);
34412#endif
34413}
34414
34415
34416static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
34417{
34418 float** dst_f32 = (float**)dst;
34419 const float* src_f32 = (const float*)src;
34420
34421 ma_uint64 iFrame;
34422 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34423 ma_uint32 iChannel;
34424 for (iChannel = 0; iChannel < channels; iChannel += 1) {
34425 dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel];
34426 }
34427 }
34428}
34429
34430static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
34431{
34432 ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
34433}
34434
34435void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
34436{
34437#ifdef MA_USE_REFERENCE_CONVERSION_APIS
34438 ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
34439#else
34440 ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels);
34441#endif
34442}
34443
34444
34445void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
34446{
34447 if (formatOut == formatIn) {
34448 ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut));
34449 return;
34450 }
34451
34452 switch (formatIn)
34453 {
34454 case ma_format_u8:
34455 {
34456 switch (formatOut)
34457 {
34458 case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return;
34459 case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return;
34460 case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return;
34461 case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return;
34462 default: break;
34463 }
34464 } break;
34465
34466 case ma_format_s16:
34467 {
34468 switch (formatOut)
34469 {
34470 case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return;
34471 case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return;
34472 case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return;
34473 case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return;
34474 default: break;
34475 }
34476 } break;
34477
34478 case ma_format_s24:
34479 {
34480 switch (formatOut)
34481 {
34482 case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return;
34483 case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return;
34484 case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return;
34485 case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return;
34486 default: break;
34487 }
34488 } break;
34489
34490 case ma_format_s32:
34491 {
34492 switch (formatOut)
34493 {
34494 case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
34495 case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
34496 case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
34497 case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return;
34498 default: break;
34499 }
34500 } break;
34501
34502 case ma_format_f32:
34503 {
34504 switch (formatOut)
34505 {
34506 case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
34507 case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
34508 case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
34509 case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return;
34510 default: break;
34511 }
34512 } break;
34513
34514 default: break;
34515 }
34516}
34517
34518void 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)
34519{
34520 ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode);
34521}
34522
34523void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)
34524{
34525 if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) {
34526 return; /* Invalid args. */
34527 }
34528
34529 /* For efficiency we do this per format. */
34530 switch (format) {
34531 case ma_format_s16:
34532 {
34533 const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames;
34534 ma_uint64 iPCMFrame;
34535 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
34536 ma_uint32 iChannel;
34537 for (iChannel = 0; iChannel < channels; ++iChannel) {
34538 ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel];
34539 pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel];
34540 }
34541 }
34542 } break;
34543
34544 case ma_format_f32:
34545 {
34546 const float* pSrcF32 = (const float*)pInterleavedPCMFrames;
34547 ma_uint64 iPCMFrame;
34548 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
34549 ma_uint32 iChannel;
34550 for (iChannel = 0; iChannel < channels; ++iChannel) {
34551 float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel];
34552 pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel];
34553 }
34554 }
34555 } break;
34556
34557 default:
34558 {
34559 ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
34560 ma_uint64 iPCMFrame;
34561 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
34562 ma_uint32 iChannel;
34563 for (iChannel = 0; iChannel < channels; ++iChannel) {
34564 void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
34565 const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
34566 memcpy(pDst, pSrc, sampleSizeInBytes);
34567 }
34568 }
34569 } break;
34570 }
34571}
34572
34573void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)
34574{
34575 switch (format)
34576 {
34577 case ma_format_s16:
34578 {
34579 ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames;
34580 ma_uint64 iPCMFrame;
34581 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
34582 ma_uint32 iChannel;
34583 for (iChannel = 0; iChannel < channels; ++iChannel) {
34584 const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel];
34585 pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame];
34586 }
34587 }
34588 } break;
34589
34590 case ma_format_f32:
34591 {
34592 float* pDstF32 = (float*)pInterleavedPCMFrames;
34593 ma_uint64 iPCMFrame;
34594 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
34595 ma_uint32 iChannel;
34596 for (iChannel = 0; iChannel < channels; ++iChannel) {
34597 const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel];
34598 pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame];
34599 }
34600 }
34601 } break;
34602
34603 default:
34604 {
34605 ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
34606 ma_uint64 iPCMFrame;
34607 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
34608 ma_uint32 iChannel;
34609 for (iChannel = 0; iChannel < channels; ++iChannel) {
34610 void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
34611 const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
34612 memcpy(pDst, pSrc, sampleSizeInBytes);
34613 }
34614 }
34615 } break;
34616 }
34617}
34618
34619
34620
34621/**************************************************************************************************************************************************************
34622
34623Channel Maps
34624
34625**************************************************************************************************************************************************************/
34626static void ma_get_standard_channel_map_microsoft(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
34627{
34628 /* Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */
34629 switch (channels)
34630 {
34631 case 1:
34632 {
34633 channelMap[0] = MA_CHANNEL_MONO;
34634 } break;
34635
34636 case 2:
34637 {
34638 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34639 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34640 } break;
34641
34642 case 3: /* Not defined, but best guess. */
34643 {
34644 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34645 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34646 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
34647 } break;
34648
34649 case 4:
34650 {
34651#ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP
34652 /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */
34653 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34654 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34655 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
34656 channelMap[3] = MA_CHANNEL_BACK_CENTER;
34657#else
34658 /* Quad. */
34659 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34660 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34661 channelMap[2] = MA_CHANNEL_BACK_LEFT;
34662 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
34663#endif
34664 } break;
34665
34666 case 5: /* Not defined, but best guess. */
34667 {
34668 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34669 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34670 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
34671 channelMap[3] = MA_CHANNEL_BACK_LEFT;
34672 channelMap[4] = MA_CHANNEL_BACK_RIGHT;
34673 } break;
34674
34675 case 6:
34676 {
34677 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34678 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34679 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
34680 channelMap[3] = MA_CHANNEL_LFE;
34681 channelMap[4] = MA_CHANNEL_SIDE_LEFT;
34682 channelMap[5] = MA_CHANNEL_SIDE_RIGHT;
34683 } break;
34684
34685 case 7: /* Not defined, but best guess. */
34686 {
34687 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34688 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34689 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
34690 channelMap[3] = MA_CHANNEL_LFE;
34691 channelMap[4] = MA_CHANNEL_BACK_CENTER;
34692 channelMap[5] = MA_CHANNEL_SIDE_LEFT;
34693 channelMap[6] = MA_CHANNEL_SIDE_RIGHT;
34694 } break;
34695
34696 case 8:
34697 default:
34698 {
34699 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34700 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34701 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
34702 channelMap[3] = MA_CHANNEL_LFE;
34703 channelMap[4] = MA_CHANNEL_BACK_LEFT;
34704 channelMap[5] = MA_CHANNEL_BACK_RIGHT;
34705 channelMap[6] = MA_CHANNEL_SIDE_LEFT;
34706 channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
34707 } break;
34708 }
34709
34710 /* Remainder. */
34711 if (channels > 8) {
34712 ma_uint32 iChannel;
34713 for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
34714 channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
34715 }
34716 }
34717}
34718
34719static void ma_get_standard_channel_map_alsa(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
34720{
34721 switch (channels)
34722 {
34723 case 1:
34724 {
34725 channelMap[0] = MA_CHANNEL_MONO;
34726 } break;
34727
34728 case 2:
34729 {
34730 channelMap[0] = MA_CHANNEL_LEFT;
34731 channelMap[1] = MA_CHANNEL_RIGHT;
34732 } break;
34733
34734 case 3:
34735 {
34736 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34737 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34738 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
34739 } break;
34740
34741 case 4:
34742 {
34743 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34744 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34745 channelMap[2] = MA_CHANNEL_BACK_LEFT;
34746 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
34747 } break;
34748
34749 case 5:
34750 {
34751 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34752 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34753 channelMap[2] = MA_CHANNEL_BACK_LEFT;
34754 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
34755 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
34756 } break;
34757
34758 case 6:
34759 {
34760 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34761 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34762 channelMap[2] = MA_CHANNEL_BACK_LEFT;
34763 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
34764 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
34765 channelMap[5] = MA_CHANNEL_LFE;
34766 } break;
34767
34768 case 7:
34769 {
34770 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34771 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34772 channelMap[2] = MA_CHANNEL_BACK_LEFT;
34773 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
34774 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
34775 channelMap[5] = MA_CHANNEL_LFE;
34776 channelMap[6] = MA_CHANNEL_BACK_CENTER;
34777 } break;
34778
34779 case 8:
34780 default:
34781 {
34782 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34783 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34784 channelMap[2] = MA_CHANNEL_BACK_LEFT;
34785 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
34786 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
34787 channelMap[5] = MA_CHANNEL_LFE;
34788 channelMap[6] = MA_CHANNEL_SIDE_LEFT;
34789 channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
34790 } break;
34791 }
34792
34793 /* Remainder. */
34794 if (channels > 8) {
34795 ma_uint32 iChannel;
34796 for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
34797 channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
34798 }
34799 }
34800}
34801
34802static void ma_get_standard_channel_map_rfc3551(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
34803{
34804 switch (channels)
34805 {
34806 case 1:
34807 {
34808 channelMap[0] = MA_CHANNEL_MONO;
34809 } break;
34810
34811 case 2:
34812 {
34813 channelMap[0] = MA_CHANNEL_LEFT;
34814 channelMap[1] = MA_CHANNEL_RIGHT;
34815 } break;
34816
34817 case 3:
34818 {
34819 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34820 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34821 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
34822 } break;
34823
34824 case 4:
34825 {
34826 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34827 channelMap[1] = MA_CHANNEL_FRONT_CENTER;
34828 channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
34829 channelMap[3] = MA_CHANNEL_BACK_CENTER;
34830 } break;
34831
34832 case 5:
34833 {
34834 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34835 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34836 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
34837 channelMap[3] = MA_CHANNEL_BACK_LEFT;
34838 channelMap[4] = MA_CHANNEL_BACK_RIGHT;
34839 } break;
34840
34841 case 6:
34842 {
34843 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34844 channelMap[1] = MA_CHANNEL_SIDE_LEFT;
34845 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
34846 channelMap[3] = MA_CHANNEL_FRONT_RIGHT;
34847 channelMap[4] = MA_CHANNEL_SIDE_RIGHT;
34848 channelMap[5] = MA_CHANNEL_BACK_CENTER;
34849 } break;
34850 }
34851
34852 /* Remainder. */
34853 if (channels > 8) {
34854 ma_uint32 iChannel;
34855 for (iChannel = 6; iChannel < MA_MAX_CHANNELS; ++iChannel) {
34856 channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6));
34857 }
34858 }
34859}
34860
34861static void ma_get_standard_channel_map_flac(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
34862{
34863 switch (channels)
34864 {
34865 case 1:
34866 {
34867 channelMap[0] = MA_CHANNEL_MONO;
34868 } break;
34869
34870 case 2:
34871 {
34872 channelMap[0] = MA_CHANNEL_LEFT;
34873 channelMap[1] = MA_CHANNEL_RIGHT;
34874 } break;
34875
34876 case 3:
34877 {
34878 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34879 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34880 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
34881 } break;
34882
34883 case 4:
34884 {
34885 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34886 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34887 channelMap[2] = MA_CHANNEL_BACK_LEFT;
34888 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
34889 } break;
34890
34891 case 5:
34892 {
34893 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34894 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34895 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
34896 channelMap[3] = MA_CHANNEL_BACK_LEFT;
34897 channelMap[4] = MA_CHANNEL_BACK_RIGHT;
34898 } break;
34899
34900 case 6:
34901 {
34902 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34903 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34904 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
34905 channelMap[3] = MA_CHANNEL_LFE;
34906 channelMap[4] = MA_CHANNEL_BACK_LEFT;
34907 channelMap[5] = MA_CHANNEL_BACK_RIGHT;
34908 } break;
34909
34910 case 7:
34911 {
34912 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34913 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34914 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
34915 channelMap[3] = MA_CHANNEL_LFE;
34916 channelMap[4] = MA_CHANNEL_BACK_CENTER;
34917 channelMap[5] = MA_CHANNEL_SIDE_LEFT;
34918 channelMap[6] = MA_CHANNEL_SIDE_RIGHT;
34919 } break;
34920
34921 case 8:
34922 default:
34923 {
34924 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34925 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34926 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
34927 channelMap[3] = MA_CHANNEL_LFE;
34928 channelMap[4] = MA_CHANNEL_BACK_LEFT;
34929 channelMap[5] = MA_CHANNEL_BACK_RIGHT;
34930 channelMap[6] = MA_CHANNEL_SIDE_LEFT;
34931 channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
34932 } break;
34933 }
34934
34935 /* Remainder. */
34936 if (channels > 8) {
34937 ma_uint32 iChannel;
34938 for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
34939 channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
34940 }
34941 }
34942}
34943
34944static void ma_get_standard_channel_map_vorbis(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
34945{
34946 /* 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?! */
34947 switch (channels)
34948 {
34949 case 1:
34950 {
34951 channelMap[0] = MA_CHANNEL_MONO;
34952 } break;
34953
34954 case 2:
34955 {
34956 channelMap[0] = MA_CHANNEL_LEFT;
34957 channelMap[1] = MA_CHANNEL_RIGHT;
34958 } break;
34959
34960 case 3:
34961 {
34962 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34963 channelMap[1] = MA_CHANNEL_FRONT_CENTER;
34964 channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
34965 } break;
34966
34967 case 4:
34968 {
34969 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34970 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
34971 channelMap[2] = MA_CHANNEL_BACK_LEFT;
34972 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
34973 } break;
34974
34975 case 5:
34976 {
34977 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34978 channelMap[1] = MA_CHANNEL_FRONT_CENTER;
34979 channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
34980 channelMap[3] = MA_CHANNEL_BACK_LEFT;
34981 channelMap[4] = MA_CHANNEL_BACK_RIGHT;
34982 } break;
34983
34984 case 6:
34985 {
34986 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34987 channelMap[1] = MA_CHANNEL_FRONT_CENTER;
34988 channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
34989 channelMap[3] = MA_CHANNEL_BACK_LEFT;
34990 channelMap[4] = MA_CHANNEL_BACK_RIGHT;
34991 channelMap[5] = MA_CHANNEL_LFE;
34992 } break;
34993
34994 case 7:
34995 {
34996 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
34997 channelMap[1] = MA_CHANNEL_FRONT_CENTER;
34998 channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
34999 channelMap[3] = MA_CHANNEL_SIDE_LEFT;
35000 channelMap[4] = MA_CHANNEL_SIDE_RIGHT;
35001 channelMap[5] = MA_CHANNEL_BACK_CENTER;
35002 channelMap[6] = MA_CHANNEL_LFE;
35003 } break;
35004
35005 case 8:
35006 default:
35007 {
35008 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
35009 channelMap[1] = MA_CHANNEL_FRONT_CENTER;
35010 channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
35011 channelMap[3] = MA_CHANNEL_SIDE_LEFT;
35012 channelMap[4] = MA_CHANNEL_SIDE_RIGHT;
35013 channelMap[5] = MA_CHANNEL_BACK_LEFT;
35014 channelMap[6] = MA_CHANNEL_BACK_RIGHT;
35015 channelMap[7] = MA_CHANNEL_LFE;
35016 } break;
35017 }
35018
35019 /* Remainder. */
35020 if (channels > 8) {
35021 ma_uint32 iChannel;
35022 for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
35023 channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
35024 }
35025 }
35026}
35027
35028static void ma_get_standard_channel_map_sound4(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
35029{
35030 switch (channels)
35031 {
35032 case 1:
35033 {
35034 channelMap[0] = MA_CHANNEL_MONO;
35035 } break;
35036
35037 case 2:
35038 {
35039 channelMap[0] = MA_CHANNEL_LEFT;
35040 channelMap[1] = MA_CHANNEL_RIGHT;
35041 } break;
35042
35043 case 3:
35044 {
35045 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
35046 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
35047 channelMap[2] = MA_CHANNEL_BACK_CENTER;
35048 } break;
35049
35050 case 4:
35051 {
35052 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
35053 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
35054 channelMap[2] = MA_CHANNEL_BACK_LEFT;
35055 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
35056 } break;
35057
35058 case 5:
35059 {
35060 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
35061 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
35062 channelMap[2] = MA_CHANNEL_BACK_LEFT;
35063 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
35064 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
35065 } break;
35066
35067 case 6:
35068 {
35069 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
35070 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
35071 channelMap[2] = MA_CHANNEL_BACK_LEFT;
35072 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
35073 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
35074 channelMap[5] = MA_CHANNEL_LFE;
35075 } break;
35076
35077 case 7:
35078 {
35079 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
35080 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
35081 channelMap[2] = MA_CHANNEL_BACK_LEFT;
35082 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
35083 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
35084 channelMap[5] = MA_CHANNEL_BACK_CENTER;
35085 channelMap[6] = MA_CHANNEL_LFE;
35086 } break;
35087
35088 case 8:
35089 default:
35090 {
35091 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
35092 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
35093 channelMap[2] = MA_CHANNEL_BACK_LEFT;
35094 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
35095 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
35096 channelMap[5] = MA_CHANNEL_LFE;
35097 channelMap[6] = MA_CHANNEL_SIDE_LEFT;
35098 channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
35099 } break;
35100 }
35101
35102 /* Remainder. */
35103 if (channels > 8) {
35104 ma_uint32 iChannel;
35105 for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
35106 channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
35107 }
35108 }
35109}
35110
35111static void ma_get_standard_channel_map_sndio(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
35112{
35113 switch (channels)
35114 {
35115 case 1:
35116 {
35117 channelMap[0] = MA_CHANNEL_MONO;
35118 } break;
35119
35120 case 2:
35121 {
35122 channelMap[0] = MA_CHANNEL_LEFT;
35123 channelMap[1] = MA_CHANNEL_RIGHT;
35124 } break;
35125
35126 case 3:
35127 {
35128 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
35129 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
35130 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
35131 } break;
35132
35133 case 4:
35134 {
35135 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
35136 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
35137 channelMap[2] = MA_CHANNEL_BACK_LEFT;
35138 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
35139 } break;
35140
35141 case 5:
35142 {
35143 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
35144 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
35145 channelMap[2] = MA_CHANNEL_BACK_LEFT;
35146 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
35147 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
35148 } break;
35149
35150 case 6:
35151 default:
35152 {
35153 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
35154 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
35155 channelMap[2] = MA_CHANNEL_BACK_LEFT;
35156 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
35157 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
35158 channelMap[5] = MA_CHANNEL_LFE;
35159 } break;
35160 }
35161
35162 /* Remainder. */
35163 if (channels > 6) {
35164 ma_uint32 iChannel;
35165 for (iChannel = 6; iChannel < MA_MAX_CHANNELS; ++iChannel) {
35166 channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6));
35167 }
35168 }
35169}
35170
35171void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
35172{
35173 switch (standardChannelMap)
35174 {
35175 case ma_standard_channel_map_alsa:
35176 {
35177 ma_get_standard_channel_map_alsa(channels, channelMap);
35178 } break;
35179
35180 case ma_standard_channel_map_rfc3551:
35181 {
35182 ma_get_standard_channel_map_rfc3551(channels, channelMap);
35183 } break;
35184
35185 case ma_standard_channel_map_flac:
35186 {
35187 ma_get_standard_channel_map_flac(channels, channelMap);
35188 } break;
35189
35190 case ma_standard_channel_map_vorbis:
35191 {
35192 ma_get_standard_channel_map_vorbis(channels, channelMap);
35193 } break;
35194
35195 case ma_standard_channel_map_sound4:
35196 {
35197 ma_get_standard_channel_map_sound4(channels, channelMap);
35198 } break;
35199
35200 case ma_standard_channel_map_sndio:
35201 {
35202 ma_get_standard_channel_map_sndio(channels, channelMap);
35203 } break;
35204
35205 case ma_standard_channel_map_microsoft:
35206 default:
35207 {
35208 ma_get_standard_channel_map_microsoft(channels, channelMap);
35209 } break;
35210 }
35211}
35212
35213void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels)
35214{
35215 if (pOut != NULL && pIn != NULL && channels > 0) {
35216 MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels);
35217 }
35218}
35219
35220ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS])
35221{
35222 if (channelMap == NULL) {
35223 return MA_FALSE;
35224 }
35225
35226 /* A channel count of 0 is invalid. */
35227 if (channels == 0) {
35228 return MA_FALSE;
35229 }
35230
35231 /* It does not make sense to have a mono channel when there is more than 1 channel. */
35232 if (channels > 1) {
35233 ma_uint32 iChannel;
35234 for (iChannel = 0; iChannel < channels; ++iChannel) {
35235 if (channelMap[iChannel] == MA_CHANNEL_MONO) {
35236 return MA_FALSE;
35237 }
35238 }
35239 }
35240
35241 return MA_TRUE;
35242}
35243
35244ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel channelMapA[MA_MAX_CHANNELS], const ma_channel channelMapB[MA_MAX_CHANNELS])
35245{
35246 ma_uint32 iChannel;
35247
35248 if (channelMapA == channelMapB) {
35249 return MA_FALSE;
35250 }
35251
35252 if (channels == 0 || channels > MA_MAX_CHANNELS) {
35253 return MA_FALSE;
35254 }
35255
35256 for (iChannel = 0; iChannel < channels; ++iChannel) {
35257 if (channelMapA[iChannel] != channelMapB[iChannel]) {
35258 return MA_FALSE;
35259 }
35260 }
35261
35262 return MA_TRUE;
35263}
35264
35265ma_bool32 ma_channel_map_blank(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS])
35266{
35267 ma_uint32 iChannel;
35268
35269 for (iChannel = 0; iChannel < channels; ++iChannel) {
35270 if (channelMap[iChannel] != MA_CHANNEL_NONE) {
35271 return MA_FALSE;
35272 }
35273 }
35274
35275 return MA_TRUE;
35276}
35277
35278ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS], ma_channel channelPosition)
35279{
35280 ma_uint32 iChannel;
35281 for (iChannel = 0; iChannel < channels; ++iChannel) {
35282 if (channelMap[iChannel] == channelPosition) {
35283 return MA_TRUE;
35284 }
35285 }
35286
35287 return MA_FALSE;
35288}
35289
35290
35291
35292/**************************************************************************************************************************************************************
35293
35294Conversion Helpers
35295
35296**************************************************************************************************************************************************************/
35297ma_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)
35298{
35299 ma_data_converter_config config;
35300
35301 config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut);
35302 ma_get_standard_channel_map(ma_standard_channel_map_default, channelsOut, config.channelMapOut);
35303 ma_get_standard_channel_map(ma_standard_channel_map_default, channelsIn, config.channelMapIn);
35304 config.resampling.linear.lpfCount = ma_min(MA_DEFAULT_RESAMPLER_LPF_FILTERS, MA_MAX_RESAMPLER_LPF_FILTERS);
35305
35306 return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config);
35307}
35308
35309ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig)
35310{
35311 ma_result result;
35312 ma_data_converter converter;
35313
35314 if (frameCountIn == 0 || pConfig == NULL) {
35315 return 0;
35316 }
35317
35318 result = ma_data_converter_init(pConfig, &converter);
35319 if (result != MA_SUCCESS) {
35320 return 0; /* Failed to initialize the data converter. */
35321 }
35322
35323 if (pOut == NULL) {
35324 frameCountOut = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn);
35325 } else {
35326 result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut);
35327 if (result != MA_SUCCESS) {
35328 frameCountOut = 0;
35329 }
35330 }
35331
35332 ma_data_converter_uninit(&converter);
35333 return frameCountOut;
35334}
35335
35336
35337/**************************************************************************************************************************************************************
35338
35339Ring Buffer
35340
35341**************************************************************************************************************************************************************/
35342MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset)
35343{
35344 return encodedOffset & 0x7FFFFFFF;
35345}
35346
35347MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset)
35348{
35349 return encodedOffset & 0x80000000;
35350}
35351
35352MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB)
35353{
35354 MA_ASSERT(pRB != NULL);
35355 return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(pRB->encodedReadOffset));
35356}
35357
35358MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB)
35359{
35360 MA_ASSERT(pRB != NULL);
35361 return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(pRB->encodedWriteOffset));
35362}
35363
35364MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag)
35365{
35366 return offsetLoopFlag | offsetInBytes;
35367}
35368
35369MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag)
35370{
35371 MA_ASSERT(pOffsetInBytes != NULL);
35372 MA_ASSERT(pOffsetLoopFlag != NULL);
35373
35374 *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset);
35375 *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset);
35376}
35377
35378
35379ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB)
35380{
35381 ma_result result;
35382 const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1);
35383
35384 if (pRB == NULL) {
35385 return MA_INVALID_ARGS;
35386 }
35387
35388 if (subbufferSizeInBytes == 0 || subbufferCount == 0) {
35389 return MA_INVALID_ARGS;
35390 }
35391
35392 if (subbufferSizeInBytes > maxSubBufferSize) {
35393 return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */
35394 }
35395
35396
35397 MA_ZERO_OBJECT(pRB);
35398
35399 result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks);
35400 if (result != MA_SUCCESS) {
35401 return result;
35402 }
35403
35404 pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes;
35405 pRB->subbufferCount = (ma_uint32)subbufferCount;
35406
35407 if (pOptionalPreallocatedBuffer != NULL) {
35408 pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes;
35409 pRB->pBuffer = pOptionalPreallocatedBuffer;
35410 } else {
35411 size_t bufferSizeInBytes;
35412
35413 /*
35414 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
35415 we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT.
35416 */
35417 pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT;
35418
35419 bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes;
35420 pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks);
35421 if (pRB->pBuffer == NULL) {
35422 return MA_OUT_OF_MEMORY;
35423 }
35424
35425 MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes);
35426 pRB->ownsBuffer = MA_TRUE;
35427 }
35428
35429 return MA_SUCCESS;
35430}
35431
35432ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB)
35433{
35434 return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
35435}
35436
35437void ma_rb_uninit(ma_rb* pRB)
35438{
35439 if (pRB == NULL) {
35440 return;
35441 }
35442
35443 if (pRB->ownsBuffer) {
35444 ma_aligned_free(pRB->pBuffer, &pRB->allocationCallbacks);
35445 }
35446}
35447
35448void ma_rb_reset(ma_rb* pRB)
35449{
35450 if (pRB == NULL) {
35451 return;
35452 }
35453
35454 pRB->encodedReadOffset = 0;
35455 pRB->encodedWriteOffset = 0;
35456}
35457
35458ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
35459{
35460 ma_uint32 writeOffset;
35461 ma_uint32 writeOffsetInBytes;
35462 ma_uint32 writeOffsetLoopFlag;
35463 ma_uint32 readOffset;
35464 ma_uint32 readOffsetInBytes;
35465 ma_uint32 readOffsetLoopFlag;
35466 size_t bytesAvailable;
35467 size_t bytesRequested;
35468
35469 if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
35470 return MA_INVALID_ARGS;
35471 }
35472
35473 /* The returned buffer should never move ahead of the write pointer. */
35474 writeOffset = pRB->encodedWriteOffset;
35475 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
35476
35477 readOffset = pRB->encodedReadOffset;
35478 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
35479
35480 /*
35481 The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we
35482 can only read up to the write pointer. If not, we can only read up to the end of the buffer.
35483 */
35484 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
35485 bytesAvailable = writeOffsetInBytes - readOffsetInBytes;
35486 } else {
35487 bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes;
35488 }
35489
35490 bytesRequested = *pSizeInBytes;
35491 if (bytesRequested > bytesAvailable) {
35492 bytesRequested = bytesAvailable;
35493 }
35494
35495 *pSizeInBytes = bytesRequested;
35496 (*ppBufferOut) = ma_rb__get_read_ptr(pRB);
35497
35498 return MA_SUCCESS;
35499}
35500
35501ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut)
35502{
35503 ma_uint32 readOffset;
35504 ma_uint32 readOffsetInBytes;
35505 ma_uint32 readOffsetLoopFlag;
35506 ma_uint32 newReadOffsetInBytes;
35507 ma_uint32 newReadOffsetLoopFlag;
35508
35509 if (pRB == NULL) {
35510 return MA_INVALID_ARGS;
35511 }
35512
35513 /* Validate the buffer. */
35514 if (pBufferOut != ma_rb__get_read_ptr(pRB)) {
35515 return MA_INVALID_ARGS;
35516 }
35517
35518 readOffset = pRB->encodedReadOffset;
35519 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
35520
35521 /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
35522 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes);
35523 if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) {
35524 return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
35525 }
35526
35527 /* Move the read pointer back to the start if necessary. */
35528 newReadOffsetLoopFlag = readOffsetLoopFlag;
35529 if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) {
35530 newReadOffsetInBytes = 0;
35531 newReadOffsetLoopFlag ^= 0x80000000;
35532 }
35533
35534 ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes));
35535 return MA_SUCCESS;
35536}
35537
35538ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
35539{
35540 ma_uint32 readOffset;
35541 ma_uint32 readOffsetInBytes;
35542 ma_uint32 readOffsetLoopFlag;
35543 ma_uint32 writeOffset;
35544 ma_uint32 writeOffsetInBytes;
35545 ma_uint32 writeOffsetLoopFlag;
35546 size_t bytesAvailable;
35547 size_t bytesRequested;
35548
35549 if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
35550 return MA_INVALID_ARGS;
35551 }
35552
35553 /* The returned buffer should never overtake the read buffer. */
35554 readOffset = pRB->encodedReadOffset;
35555 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
35556
35557 writeOffset = pRB->encodedWriteOffset;
35558 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
35559
35560 /*
35561 In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only
35562 write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should
35563 never overtake the read pointer.
35564 */
35565 if (writeOffsetLoopFlag == readOffsetLoopFlag) {
35566 bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes;
35567 } else {
35568 bytesAvailable = readOffsetInBytes - writeOffsetInBytes;
35569 }
35570
35571 bytesRequested = *pSizeInBytes;
35572 if (bytesRequested > bytesAvailable) {
35573 bytesRequested = bytesAvailable;
35574 }
35575
35576 *pSizeInBytes = bytesRequested;
35577 *ppBufferOut = ma_rb__get_write_ptr(pRB);
35578
35579 /* Clear the buffer if desired. */
35580 if (pRB->clearOnWriteAcquire) {
35581 MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes);
35582 }
35583
35584 return MA_SUCCESS;
35585}
35586
35587ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut)
35588{
35589 ma_uint32 writeOffset;
35590 ma_uint32 writeOffsetInBytes;
35591 ma_uint32 writeOffsetLoopFlag;
35592 ma_uint32 newWriteOffsetInBytes;
35593 ma_uint32 newWriteOffsetLoopFlag;
35594
35595 if (pRB == NULL) {
35596 return MA_INVALID_ARGS;
35597 }
35598
35599 /* Validate the buffer. */
35600 if (pBufferOut != ma_rb__get_write_ptr(pRB)) {
35601 return MA_INVALID_ARGS;
35602 }
35603
35604 writeOffset = pRB->encodedWriteOffset;
35605 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
35606
35607 /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
35608 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes);
35609 if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) {
35610 return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
35611 }
35612
35613 /* Move the read pointer back to the start if necessary. */
35614 newWriteOffsetLoopFlag = writeOffsetLoopFlag;
35615 if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) {
35616 newWriteOffsetInBytes = 0;
35617 newWriteOffsetLoopFlag ^= 0x80000000;
35618 }
35619
35620 ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes));
35621 return MA_SUCCESS;
35622}
35623
35624ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes)
35625{
35626 ma_uint32 readOffset;
35627 ma_uint32 readOffsetInBytes;
35628 ma_uint32 readOffsetLoopFlag;
35629 ma_uint32 writeOffset;
35630 ma_uint32 writeOffsetInBytes;
35631 ma_uint32 writeOffsetLoopFlag;
35632 ma_uint32 newReadOffsetInBytes;
35633 ma_uint32 newReadOffsetLoopFlag;
35634
35635 if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) {
35636 return MA_INVALID_ARGS;
35637 }
35638
35639 readOffset = pRB->encodedReadOffset;
35640 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
35641
35642 writeOffset = pRB->encodedWriteOffset;
35643 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
35644
35645 newReadOffsetInBytes = readOffsetInBytes;
35646 newReadOffsetLoopFlag = readOffsetLoopFlag;
35647
35648 /* We cannot go past the write buffer. */
35649 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
35650 if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) {
35651 newReadOffsetInBytes = writeOffsetInBytes;
35652 } else {
35653 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
35654 }
35655 } else {
35656 /* May end up looping. */
35657 if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
35658 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
35659 newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
35660 } else {
35661 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
35662 }
35663 }
35664
35665 ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag));
35666 return MA_SUCCESS;
35667}
35668
35669ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes)
35670{
35671 ma_uint32 readOffset;
35672 ma_uint32 readOffsetInBytes;
35673 ma_uint32 readOffsetLoopFlag;
35674 ma_uint32 writeOffset;
35675 ma_uint32 writeOffsetInBytes;
35676 ma_uint32 writeOffsetLoopFlag;
35677 ma_uint32 newWriteOffsetInBytes;
35678 ma_uint32 newWriteOffsetLoopFlag;
35679
35680 if (pRB == NULL) {
35681 return MA_INVALID_ARGS;
35682 }
35683
35684 readOffset = pRB->encodedReadOffset;
35685 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
35686
35687 writeOffset = pRB->encodedWriteOffset;
35688 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
35689
35690 newWriteOffsetInBytes = writeOffsetInBytes;
35691 newWriteOffsetLoopFlag = writeOffsetLoopFlag;
35692
35693 /* We cannot go past the write buffer. */
35694 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
35695 /* May end up looping. */
35696 if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
35697 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
35698 newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
35699 } else {
35700 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
35701 }
35702 } else {
35703 if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) {
35704 newWriteOffsetInBytes = readOffsetInBytes;
35705 } else {
35706 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
35707 }
35708 }
35709
35710 ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag));
35711 return MA_SUCCESS;
35712}
35713
35714ma_int32 ma_rb_pointer_distance(ma_rb* pRB)
35715{
35716 ma_uint32 readOffset;
35717 ma_uint32 readOffsetInBytes;
35718 ma_uint32 readOffsetLoopFlag;
35719 ma_uint32 writeOffset;
35720 ma_uint32 writeOffsetInBytes;
35721 ma_uint32 writeOffsetLoopFlag;
35722
35723 if (pRB == NULL) {
35724 return 0;
35725 }
35726
35727 readOffset = pRB->encodedReadOffset;
35728 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
35729
35730 writeOffset = pRB->encodedWriteOffset;
35731 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
35732
35733 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
35734 return writeOffsetInBytes - readOffsetInBytes;
35735 } else {
35736 return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes);
35737 }
35738}
35739
35740ma_uint32 ma_rb_available_read(ma_rb* pRB)
35741{
35742 ma_int32 dist;
35743
35744 if (pRB == NULL) {
35745 return 0;
35746 }
35747
35748 dist = ma_rb_pointer_distance(pRB);
35749 if (dist < 0) {
35750 return 0;
35751 }
35752
35753 return dist;
35754}
35755
35756ma_uint32 ma_rb_available_write(ma_rb* pRB)
35757{
35758 if (pRB == NULL) {
35759 return 0;
35760 }
35761
35762 return (ma_uint32)(ma_rb_get_subbuffer_size(pRB) - ma_rb_pointer_distance(pRB));
35763}
35764
35765size_t ma_rb_get_subbuffer_size(ma_rb* pRB)
35766{
35767 if (pRB == NULL) {
35768 return 0;
35769 }
35770
35771 return pRB->subbufferSizeInBytes;
35772}
35773
35774size_t ma_rb_get_subbuffer_stride(ma_rb* pRB)
35775{
35776 if (pRB == NULL) {
35777 return 0;
35778 }
35779
35780 if (pRB->subbufferStrideInBytes == 0) {
35781 return (size_t)pRB->subbufferSizeInBytes;
35782 }
35783
35784 return (size_t)pRB->subbufferStrideInBytes;
35785}
35786
35787size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex)
35788{
35789 if (pRB == NULL) {
35790 return 0;
35791 }
35792
35793 return subbufferIndex * ma_rb_get_subbuffer_stride(pRB);
35794}
35795
35796void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer)
35797{
35798 if (pRB == NULL) {
35799 return NULL;
35800 }
35801
35802 return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex));
35803}
35804
35805
35806static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB)
35807{
35808 MA_ASSERT(pRB != NULL);
35809
35810 return ma_get_bytes_per_frame(pRB->format, pRB->channels);
35811}
35812
35813ma_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)
35814{
35815 ma_uint32 bpf;
35816 ma_result result;
35817
35818 if (pRB == NULL) {
35819 return MA_INVALID_ARGS;
35820 }
35821
35822 MA_ZERO_OBJECT(pRB);
35823
35824 bpf = ma_get_bytes_per_frame(format, channels);
35825 if (bpf == 0) {
35826 return MA_INVALID_ARGS;
35827 }
35828
35829 result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb);
35830 if (result != MA_SUCCESS) {
35831 return result;
35832 }
35833
35834 pRB->format = format;
35835 pRB->channels = channels;
35836
35837 return MA_SUCCESS;
35838}
35839
35840ma_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)
35841{
35842 return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
35843}
35844
35845void ma_pcm_rb_uninit(ma_pcm_rb* pRB)
35846{
35847 if (pRB == NULL) {
35848 return;
35849 }
35850
35851 ma_rb_uninit(&pRB->rb);
35852}
35853
35854void ma_pcm_rb_reset(ma_pcm_rb* pRB)
35855{
35856 if (pRB == NULL) {
35857 return;
35858 }
35859
35860 ma_rb_reset(&pRB->rb);
35861}
35862
35863ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
35864{
35865 size_t sizeInBytes;
35866 ma_result result;
35867
35868 if (pRB == NULL || pSizeInFrames == NULL) {
35869 return MA_INVALID_ARGS;
35870 }
35871
35872 sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
35873
35874 result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut);
35875 if (result != MA_SUCCESS) {
35876 return result;
35877 }
35878
35879 *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB));
35880 return MA_SUCCESS;
35881}
35882
35883ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut)
35884{
35885 if (pRB == NULL) {
35886 return MA_INVALID_ARGS;
35887 }
35888
35889 return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB), pBufferOut);
35890}
35891
35892ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
35893{
35894 size_t sizeInBytes;
35895 ma_result result;
35896
35897 if (pRB == NULL) {
35898 return MA_INVALID_ARGS;
35899 }
35900
35901 sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
35902
35903 result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut);
35904 if (result != MA_SUCCESS) {
35905 return result;
35906 }
35907
35908 *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB));
35909 return MA_SUCCESS;
35910}
35911
35912ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut)
35913{
35914 if (pRB == NULL) {
35915 return MA_INVALID_ARGS;
35916 }
35917
35918 return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB), pBufferOut);
35919}
35920
35921ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
35922{
35923 if (pRB == NULL) {
35924 return MA_INVALID_ARGS;
35925 }
35926
35927 return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
35928}
35929
35930ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
35931{
35932 if (pRB == NULL) {
35933 return MA_INVALID_ARGS;
35934 }
35935
35936 return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
35937}
35938
35939ma_int32 ma_pcm_rb_pointer_disance(ma_pcm_rb* pRB)
35940{
35941 if (pRB == NULL) {
35942 return 0;
35943 }
35944
35945 return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
35946}
35947
35948ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB)
35949{
35950 if (pRB == NULL) {
35951 return 0;
35952 }
35953
35954 return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
35955}
35956
35957ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB)
35958{
35959 if (pRB == NULL) {
35960 return 0;
35961 }
35962
35963 return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
35964}
35965
35966ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB)
35967{
35968 if (pRB == NULL) {
35969 return 0;
35970 }
35971
35972 return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
35973}
35974
35975ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB)
35976{
35977 if (pRB == NULL) {
35978 return 0;
35979 }
35980
35981 return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
35982}
35983
35984ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex)
35985{
35986 if (pRB == NULL) {
35987 return 0;
35988 }
35989
35990 return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB));
35991}
35992
35993void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer)
35994{
35995 if (pRB == NULL) {
35996 return NULL;
35997 }
35998
35999 return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer);
36000}
36001
36002
36003
36004/**************************************************************************************************************************************************************
36005
36006Miscellaneous Helpers
36007
36008**************************************************************************************************************************************************************/
36009void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
36010{
36011 if (pAllocationCallbacks != NULL) {
36012 return ma__malloc_from_callbacks(sz, pAllocationCallbacks);
36013 } else {
36014 return ma__malloc_default(sz, NULL);
36015 }
36016}
36017
36018void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
36019{
36020 if (pAllocationCallbacks != NULL) {
36021 if (pAllocationCallbacks->onRealloc != NULL) {
36022 return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData);
36023 } else {
36024 return NULL; /* This requires a native implementation of realloc(). */
36025 }
36026 } else {
36027 return ma__realloc_default(p, sz, NULL);
36028 }
36029}
36030
36031void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
36032{
36033 if (pAllocationCallbacks != NULL) {
36034 ma__free_from_callbacks(p, pAllocationCallbacks);
36035 } else {
36036 ma__free_default(p, NULL);
36037 }
36038}
36039
36040void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks)
36041{
36042 size_t extraBytes;
36043 void* pUnaligned;
36044 void* pAligned;
36045
36046 if (alignment == 0) {
36047 return 0;
36048 }
36049
36050 extraBytes = alignment-1 + sizeof(void*);
36051
36052 pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks);
36053 if (pUnaligned == NULL) {
36054 return NULL;
36055 }
36056
36057 pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1)));
36058 ((void**)pAligned)[-1] = pUnaligned;
36059
36060 return pAligned;
36061}
36062
36063void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
36064{
36065 ma_free(((void**)p)[-1], pAllocationCallbacks);
36066}
36067
36068const char* ma_get_format_name(ma_format format)
36069{
36070 switch (format)
36071 {
36072 case ma_format_unknown: return "Unknown";
36073 case ma_format_u8: return "8-bit Unsigned Integer";
36074 case ma_format_s16: return "16-bit Signed Integer";
36075 case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)";
36076 case ma_format_s32: return "32-bit Signed Integer";
36077 case ma_format_f32: return "32-bit IEEE Floating Point";
36078 default: return "Invalid";
36079 }
36080}
36081
36082void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels)
36083{
36084 ma_uint32 i;
36085 for (i = 0; i < channels; ++i) {
36086 pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor);
36087 }
36088}
36089
36090
36091ma_uint32 ma_get_bytes_per_sample(ma_format format)
36092{
36093 ma_uint32 sizes[] = {
36094 0, /* unknown */
36095 1, /* u8 */
36096 2, /* s16 */
36097 3, /* s24 */
36098 4, /* s32 */
36099 4, /* f32 */
36100 };
36101 return sizes[format];
36102}
36103
36104
36105/**************************************************************************************************************************************************************
36106
36107Decoding
36108
36109**************************************************************************************************************************************************************/
36110#ifndef MA_NO_DECODING
36111
36112static size_t ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
36113{
36114 size_t bytesRead;
36115
36116 MA_ASSERT(pDecoder != NULL);
36117 MA_ASSERT(pBufferOut != NULL);
36118
36119 bytesRead = pDecoder->onRead(pDecoder, pBufferOut, bytesToRead);
36120 pDecoder->readPointer += bytesRead;
36121
36122 return bytesRead;
36123}
36124
36125static ma_bool32 ma_decoder_seek_bytes(ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin)
36126{
36127 ma_bool32 wasSuccessful;
36128
36129 MA_ASSERT(pDecoder != NULL);
36130
36131 wasSuccessful = pDecoder->onSeek(pDecoder, byteOffset, origin);
36132 if (wasSuccessful) {
36133 if (origin == ma_seek_origin_start) {
36134 pDecoder->readPointer = (ma_uint64)byteOffset;
36135 } else {
36136 pDecoder->readPointer += byteOffset;
36137 }
36138 }
36139
36140 return wasSuccessful;
36141}
36142
36143
36144ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)
36145{
36146 ma_decoder_config config;
36147 MA_ZERO_OBJECT(&config);
36148 config.format = outputFormat;
36149 config.channels = outputChannels;
36150 config.sampleRate = outputSampleRate;
36151 ma_get_standard_channel_map(ma_standard_channel_map_default, config.channels, config.channelMap);
36152 config.resampling.algorithm = ma_resample_algorithm_linear;
36153 config.resampling.linear.lpfCount = ma_min(MA_DEFAULT_RESAMPLER_LPF_FILTERS, MA_MAX_RESAMPLER_LPF_FILTERS);
36154 config.resampling.speex.quality = 3;
36155
36156 return config;
36157}
36158
36159ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig)
36160{
36161 ma_decoder_config config;
36162 if (pConfig != NULL) {
36163 config = *pConfig;
36164 } else {
36165 MA_ZERO_OBJECT(&config);
36166 }
36167
36168 return config;
36169}
36170
36171static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig)
36172{
36173 ma_data_converter_config converterConfig;
36174
36175 MA_ASSERT(pDecoder != NULL);
36176
36177 /* Output format. */
36178 if (pConfig->format == ma_format_unknown) {
36179 pDecoder->outputFormat = pDecoder->internalFormat;
36180 } else {
36181 pDecoder->outputFormat = pConfig->format;
36182 }
36183
36184 if (pConfig->channels == 0) {
36185 pDecoder->outputChannels = pDecoder->internalChannels;
36186 } else {
36187 pDecoder->outputChannels = pConfig->channels;
36188 }
36189
36190 if (pConfig->sampleRate == 0) {
36191 pDecoder->outputSampleRate = pDecoder->internalSampleRate;
36192 } else {
36193 pDecoder->outputSampleRate = pConfig->sampleRate;
36194 }
36195
36196 if (ma_channel_map_blank(pDecoder->outputChannels, pConfig->channelMap)) {
36197 ma_get_standard_channel_map(ma_standard_channel_map_default, pDecoder->outputChannels, pDecoder->outputChannelMap);
36198 } else {
36199 MA_COPY_MEMORY(pDecoder->outputChannelMap, pConfig->channelMap, sizeof(pConfig->channelMap));
36200 }
36201
36202
36203 converterConfig = ma_data_converter_config_init(
36204 pDecoder->internalFormat, pDecoder->outputFormat,
36205 pDecoder->internalChannels, pDecoder->outputChannels,
36206 pDecoder->internalSampleRate, pDecoder->outputSampleRate
36207 );
36208 ma_channel_map_copy(converterConfig.channelMapIn, pDecoder->internalChannelMap, pDecoder->internalChannels);
36209 ma_channel_map_copy(converterConfig.channelMapOut, pDecoder->outputChannelMap, pDecoder->outputChannels);
36210 converterConfig.channelMixMode = pConfig->channelMixMode;
36211 converterConfig.ditherMode = pConfig->ditherMode;
36212 converterConfig.resampling.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */
36213 converterConfig.resampling.algorithm = pConfig->resampling.algorithm;
36214 converterConfig.resampling.linear.lpfCount = pConfig->resampling.linear.lpfCount;
36215 converterConfig.resampling.speex.quality = pConfig->resampling.speex.quality;
36216
36217 return ma_data_converter_init(&converterConfig, &pDecoder->converter);
36218}
36219
36220/* WAV */
36221#ifdef dr_wav_h
36222#define MA_HAS_WAV
36223
36224static size_t ma_decoder_internal_on_read__wav(void* pUserData, void* pBufferOut, size_t bytesToRead)
36225{
36226 ma_decoder* pDecoder = (ma_decoder*)pUserData;
36227 MA_ASSERT(pDecoder != NULL);
36228
36229 return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead);
36230}
36231
36232static drwav_bool32 ma_decoder_internal_on_seek__wav(void* pUserData, int offset, drwav_seek_origin origin)
36233{
36234 ma_decoder* pDecoder = (ma_decoder*)pUserData;
36235 MA_ASSERT(pDecoder != NULL);
36236
36237 return ma_decoder_seek_bytes(pDecoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
36238}
36239
36240static ma_uint64 ma_decoder_internal_on_read_pcm_frames__wav(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
36241{
36242 drwav* pWav;
36243
36244 MA_ASSERT(pDecoder != NULL);
36245 MA_ASSERT(pFramesOut != NULL);
36246
36247 pWav = (drwav*)pDecoder->pInternalDecoder;
36248 MA_ASSERT(pWav != NULL);
36249
36250 switch (pDecoder->internalFormat) {
36251 case ma_format_s16: return drwav_read_pcm_frames_s16(pWav, frameCount, (drwav_int16*)pFramesOut);
36252 case ma_format_s32: return drwav_read_pcm_frames_s32(pWav, frameCount, (drwav_int32*)pFramesOut);
36253 case ma_format_f32: return drwav_read_pcm_frames_f32(pWav, frameCount, (float*)pFramesOut);
36254 default: break;
36255 }
36256
36257 /* Should never get here. If we do, it means the internal format was not set correctly at initialization time. */
36258 MA_ASSERT(MA_FALSE);
36259 return 0;
36260}
36261
36262static ma_result ma_decoder_internal_on_seek_to_pcm_frame__wav(ma_decoder* pDecoder, ma_uint64 frameIndex)
36263{
36264 drwav* pWav;
36265 drwav_bool32 result;
36266
36267 pWav = (drwav*)pDecoder->pInternalDecoder;
36268 MA_ASSERT(pWav != NULL);
36269
36270 result = drwav_seek_to_pcm_frame(pWav, frameIndex);
36271 if (result) {
36272 return MA_SUCCESS;
36273 } else {
36274 return MA_ERROR;
36275 }
36276}
36277
36278static ma_result ma_decoder_internal_on_uninit__wav(ma_decoder* pDecoder)
36279{
36280 drwav_uninit((drwav*)pDecoder->pInternalDecoder);
36281 ma__free_from_callbacks(pDecoder->pInternalDecoder, &pDecoder->allocationCallbacks);
36282 return MA_SUCCESS;
36283}
36284
36285static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__wav(ma_decoder* pDecoder)
36286{
36287 return ((drwav*)pDecoder->pInternalDecoder)->totalPCMFrameCount;
36288}
36289
36290static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
36291{
36292 drwav* pWav;
36293 drwav_allocation_callbacks allocationCallbacks;
36294
36295 MA_ASSERT(pConfig != NULL);
36296 MA_ASSERT(pDecoder != NULL);
36297
36298 pWav = (drwav*)ma__malloc_from_callbacks(sizeof(*pWav), &pDecoder->allocationCallbacks);
36299 if (pWav == NULL) {
36300 return MA_OUT_OF_MEMORY;
36301 }
36302
36303 allocationCallbacks.pUserData = pDecoder->allocationCallbacks.pUserData;
36304 allocationCallbacks.onMalloc = pDecoder->allocationCallbacks.onMalloc;
36305 allocationCallbacks.onRealloc = pDecoder->allocationCallbacks.onRealloc;
36306 allocationCallbacks.onFree = pDecoder->allocationCallbacks.onFree;
36307
36308 /* Try opening the decoder first. */
36309 if (!drwav_init(pWav, ma_decoder_internal_on_read__wav, ma_decoder_internal_on_seek__wav, pDecoder, &allocationCallbacks)) {
36310 ma__free_from_callbacks(pWav, &pDecoder->allocationCallbacks);
36311 return MA_ERROR;
36312 }
36313
36314 /* If we get here it means we successfully initialized the WAV decoder. We can now initialize the rest of the ma_decoder. */
36315 pDecoder->onReadPCMFrames = ma_decoder_internal_on_read_pcm_frames__wav;
36316 pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__wav;
36317 pDecoder->onUninit = ma_decoder_internal_on_uninit__wav;
36318 pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__wav;
36319 pDecoder->pInternalDecoder = pWav;
36320
36321 /* Try to be as optimal as possible for the internal format. If miniaudio does not support a format we will fall back to f32. */
36322 pDecoder->internalFormat = ma_format_unknown;
36323 switch (pWav->translatedFormatTag) {
36324 case DR_WAVE_FORMAT_PCM:
36325 {
36326 if (pWav->bitsPerSample == 8) {
36327 pDecoder->internalFormat = ma_format_s16;
36328 } else if (pWav->bitsPerSample == 16) {
36329 pDecoder->internalFormat = ma_format_s16;
36330 } else if (pWav->bitsPerSample == 32) {
36331 pDecoder->internalFormat = ma_format_s32;
36332 }
36333 } break;
36334
36335 case DR_WAVE_FORMAT_IEEE_FLOAT:
36336 {
36337 if (pWav->bitsPerSample == 32) {
36338 pDecoder->internalFormat = ma_format_f32;
36339 }
36340 } break;
36341
36342 case DR_WAVE_FORMAT_ALAW:
36343 case DR_WAVE_FORMAT_MULAW:
36344 case DR_WAVE_FORMAT_ADPCM:
36345 case DR_WAVE_FORMAT_DVI_ADPCM:
36346 {
36347 pDecoder->internalFormat = ma_format_s16;
36348 } break;
36349 }
36350
36351 if (pDecoder->internalFormat == ma_format_unknown) {
36352 pDecoder->internalFormat = ma_format_f32;
36353 }
36354
36355 pDecoder->internalChannels = pWav->channels;
36356 pDecoder->internalSampleRate = pWav->sampleRate;
36357 ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDecoder->internalChannels, pDecoder->internalChannelMap);
36358
36359 return MA_SUCCESS;
36360}
36361#endif /* dr_wav_h */
36362
36363/* FLAC */
36364#ifdef dr_flac_h
36365#define MA_HAS_FLAC
36366
36367static size_t ma_decoder_internal_on_read__flac(void* pUserData, void* pBufferOut, size_t bytesToRead)
36368{
36369 ma_decoder* pDecoder = (ma_decoder*)pUserData;
36370 MA_ASSERT(pDecoder != NULL);
36371
36372 return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead);
36373}
36374
36375static drflac_bool32 ma_decoder_internal_on_seek__flac(void* pUserData, int offset, drflac_seek_origin origin)
36376{
36377 ma_decoder* pDecoder = (ma_decoder*)pUserData;
36378 MA_ASSERT(pDecoder != NULL);
36379
36380 return ma_decoder_seek_bytes(pDecoder, offset, (origin == drflac_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
36381}
36382
36383static ma_uint64 ma_decoder_internal_on_read_pcm_frames__flac(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
36384{
36385 drflac* pFlac;
36386
36387 MA_ASSERT(pDecoder != NULL);
36388 MA_ASSERT(pFramesOut != NULL);
36389
36390 pFlac = (drflac*)pDecoder->pInternalDecoder;
36391 MA_ASSERT(pFlac != NULL);
36392
36393 switch (pDecoder->internalFormat) {
36394 case ma_format_s16: return drflac_read_pcm_frames_s16(pFlac, frameCount, (drflac_int16*)pFramesOut);
36395 case ma_format_s32: return drflac_read_pcm_frames_s32(pFlac, frameCount, (drflac_int32*)pFramesOut);
36396 case ma_format_f32: return drflac_read_pcm_frames_f32(pFlac, frameCount, (float*)pFramesOut);
36397 default: break;
36398 }
36399
36400 /* Should never get here. If we do, it means the internal format was not set correctly at initialization time. */
36401 MA_ASSERT(MA_FALSE);
36402 return 0;
36403}
36404
36405static ma_result ma_decoder_internal_on_seek_to_pcm_frame__flac(ma_decoder* pDecoder, ma_uint64 frameIndex)
36406{
36407 drflac* pFlac;
36408 drflac_bool32 result;
36409
36410 pFlac = (drflac*)pDecoder->pInternalDecoder;
36411 MA_ASSERT(pFlac != NULL);
36412
36413 result = drflac_seek_to_pcm_frame(pFlac, frameIndex);
36414 if (result) {
36415 return MA_SUCCESS;
36416 } else {
36417 return MA_ERROR;
36418 }
36419}
36420
36421static ma_result ma_decoder_internal_on_uninit__flac(ma_decoder* pDecoder)
36422{
36423 drflac_close((drflac*)pDecoder->pInternalDecoder);
36424 return MA_SUCCESS;
36425}
36426
36427static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__flac(ma_decoder* pDecoder)
36428{
36429 return ((drflac*)pDecoder->pInternalDecoder)->totalPCMFrameCount;
36430}
36431
36432static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
36433{
36434 drflac* pFlac;
36435 drflac_allocation_callbacks allocationCallbacks;
36436
36437 MA_ASSERT(pConfig != NULL);
36438 MA_ASSERT(pDecoder != NULL);
36439
36440 allocationCallbacks.pUserData = pDecoder->allocationCallbacks.pUserData;
36441 allocationCallbacks.onMalloc = pDecoder->allocationCallbacks.onMalloc;
36442 allocationCallbacks.onRealloc = pDecoder->allocationCallbacks.onRealloc;
36443 allocationCallbacks.onFree = pDecoder->allocationCallbacks.onFree;
36444
36445 /* Try opening the decoder first. */
36446 pFlac = drflac_open(ma_decoder_internal_on_read__flac, ma_decoder_internal_on_seek__flac, pDecoder, &allocationCallbacks);
36447 if (pFlac == NULL) {
36448 return MA_ERROR;
36449 }
36450
36451 /* If we get here it means we successfully initialized the FLAC decoder. We can now initialize the rest of the ma_decoder. */
36452 pDecoder->onReadPCMFrames = ma_decoder_internal_on_read_pcm_frames__flac;
36453 pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__flac;
36454 pDecoder->onUninit = ma_decoder_internal_on_uninit__flac;
36455 pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__flac;
36456 pDecoder->pInternalDecoder = pFlac;
36457
36458 /*
36459 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
36460 since it's the only one that's truly lossless.
36461 */
36462 pDecoder->internalFormat = ma_format_s32;
36463 if (pConfig->format == ma_format_s16) {
36464 pDecoder->internalFormat = ma_format_s16;
36465 } else if (pConfig->format == ma_format_f32) {
36466 pDecoder->internalFormat = ma_format_f32;
36467 }
36468
36469 pDecoder->internalChannels = pFlac->channels;
36470 pDecoder->internalSampleRate = pFlac->sampleRate;
36471 ma_get_standard_channel_map(ma_standard_channel_map_flac, pDecoder->internalChannels, pDecoder->internalChannelMap);
36472
36473 return MA_SUCCESS;
36474}
36475#endif /* dr_flac_h */
36476
36477/* Vorbis */
36478#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
36479#define MA_HAS_VORBIS
36480
36481/* The size in bytes of each chunk of data to read from the Vorbis stream. */
36482#define MA_VORBIS_DATA_CHUNK_SIZE 4096
36483
36484typedef struct
36485{
36486 stb_vorbis* pInternalVorbis;
36487 ma_uint8* pData;
36488 size_t dataSize;
36489 size_t dataCapacity;
36490 ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */
36491 ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */
36492 float** ppPacketData;
36493} ma_vorbis_decoder;
36494
36495static ma_uint64 ma_vorbis_decoder_read_pcm_frames(ma_vorbis_decoder* pVorbis, ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
36496{
36497 float* pFramesOutF;
36498 ma_uint64 totalFramesRead;
36499
36500 MA_ASSERT(pVorbis != NULL);
36501 MA_ASSERT(pDecoder != NULL);
36502
36503 pFramesOutF = (float*)pFramesOut;
36504
36505 totalFramesRead = 0;
36506 while (frameCount > 0) {
36507 /* Read from the in-memory buffer first. */
36508 while (pVorbis->framesRemaining > 0 && frameCount > 0) {
36509 ma_uint32 iChannel;
36510 for (iChannel = 0; iChannel < pDecoder->internalChannels; ++iChannel) {
36511 pFramesOutF[0] = pVorbis->ppPacketData[iChannel][pVorbis->framesConsumed];
36512 pFramesOutF += 1;
36513 }
36514
36515 pVorbis->framesConsumed += 1;
36516 pVorbis->framesRemaining -= 1;
36517 frameCount -= 1;
36518 totalFramesRead += 1;
36519 }
36520
36521 if (frameCount == 0) {
36522 break;
36523 }
36524
36525 MA_ASSERT(pVorbis->framesRemaining == 0);
36526
36527 /* We've run out of cached frames, so decode the next packet and continue iteration. */
36528 do
36529 {
36530 int samplesRead;
36531 int consumedDataSize;
36532
36533 if (pVorbis->dataSize > INT_MAX) {
36534 break; /* Too big. */
36535 }
36536
36537 samplesRead = 0;
36538 consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->pInternalVorbis, pVorbis->pData, (int)pVorbis->dataSize, NULL, (float***)&pVorbis->ppPacketData, &samplesRead);
36539 if (consumedDataSize != 0) {
36540 size_t leftoverDataSize = (pVorbis->dataSize - (size_t)consumedDataSize);
36541 size_t i;
36542 for (i = 0; i < leftoverDataSize; ++i) {
36543 pVorbis->pData[i] = pVorbis->pData[i + consumedDataSize];
36544 }
36545
36546 pVorbis->dataSize = leftoverDataSize;
36547 pVorbis->framesConsumed = 0;
36548 pVorbis->framesRemaining = samplesRead;
36549 break;
36550 } else {
36551 /* Need more data. If there's any room in the existing buffer allocation fill that first. Otherwise expand. */
36552 size_t bytesRead;
36553 if (pVorbis->dataCapacity == pVorbis->dataSize) {
36554 /* No room. Expand. */
36555 size_t oldCap = pVorbis->dataCapacity;
36556 size_t newCap = pVorbis->dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE;
36557 ma_uint8* pNewData;
36558
36559 pNewData = (ma_uint8*)ma__realloc_from_callbacks(pVorbis->pData, newCap, oldCap, &pDecoder->allocationCallbacks);
36560 if (pNewData == NULL) {
36561 return totalFramesRead; /* Out of memory. */
36562 }
36563
36564 pVorbis->pData = pNewData;
36565 pVorbis->dataCapacity = newCap;
36566 }
36567
36568 /* Fill in a chunk. */
36569 bytesRead = ma_decoder_read_bytes(pDecoder, pVorbis->pData + pVorbis->dataSize, (pVorbis->dataCapacity - pVorbis->dataSize));
36570 if (bytesRead == 0) {
36571 return totalFramesRead; /* Error reading more data. */
36572 }
36573
36574 pVorbis->dataSize += bytesRead;
36575 }
36576 } while (MA_TRUE);
36577 }
36578
36579 return totalFramesRead;
36580}
36581
36582static ma_result ma_vorbis_decoder_seek_to_pcm_frame(ma_vorbis_decoder* pVorbis, ma_decoder* pDecoder, ma_uint64 frameIndex)
36583{
36584 float buffer[4096];
36585
36586 MA_ASSERT(pVorbis != NULL);
36587 MA_ASSERT(pDecoder != NULL);
36588
36589 /*
36590 This is terribly inefficient because stb_vorbis does not have a good seeking solution with it's push API. Currently this just performs
36591 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
36592 find the one containing the sample we need. Then we know exactly where to seek for stb_vorbis.
36593 */
36594 if (!ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start)) {
36595 return MA_ERROR;
36596 }
36597
36598 stb_vorbis_flush_pushdata(pVorbis->pInternalVorbis);
36599 pVorbis->framesConsumed = 0;
36600 pVorbis->framesRemaining = 0;
36601 pVorbis->dataSize = 0;
36602
36603 while (frameIndex > 0) {
36604 ma_uint32 framesRead;
36605 ma_uint32 framesToRead = ma_countof(buffer)/pDecoder->internalChannels;
36606 if (framesToRead > frameIndex) {
36607 framesToRead = (ma_uint32)frameIndex;
36608 }
36609
36610 framesRead = (ma_uint32)ma_vorbis_decoder_read_pcm_frames(pVorbis, pDecoder, buffer, framesToRead);
36611 if (framesRead == 0) {
36612 return MA_ERROR;
36613 }
36614
36615 frameIndex -= framesRead;
36616 }
36617
36618 return MA_SUCCESS;
36619}
36620
36621
36622static ma_result ma_decoder_internal_on_seek_to_pcm_frame__vorbis(ma_decoder* pDecoder, ma_uint64 frameIndex)
36623{
36624 ma_vorbis_decoder* pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
36625 MA_ASSERT(pVorbis != NULL);
36626
36627 return ma_vorbis_decoder_seek_to_pcm_frame(pVorbis, pDecoder, frameIndex);
36628}
36629
36630static ma_result ma_decoder_internal_on_uninit__vorbis(ma_decoder* pDecoder)
36631{
36632 ma_vorbis_decoder* pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
36633 MA_ASSERT(pVorbis != NULL);
36634
36635 stb_vorbis_close(pVorbis->pInternalVorbis);
36636 ma__free_from_callbacks(pVorbis->pData, &pDecoder->allocationCallbacks);
36637 ma__free_from_callbacks(pVorbis, &pDecoder->allocationCallbacks);
36638
36639 return MA_SUCCESS;
36640}
36641
36642static ma_uint64 ma_decoder_internal_on_read_pcm_frames__vorbis(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
36643{
36644 ma_vorbis_decoder* pVorbis;
36645
36646 MA_ASSERT(pDecoder != NULL);
36647 MA_ASSERT(pFramesOut != NULL);
36648 MA_ASSERT(pDecoder->internalFormat == ma_format_f32);
36649
36650 pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
36651 MA_ASSERT(pVorbis != NULL);
36652
36653 return ma_vorbis_decoder_read_pcm_frames(pVorbis, pDecoder, pFramesOut, frameCount);
36654}
36655
36656static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__vorbis(ma_decoder* pDecoder)
36657{
36658 /* No good way to do this with Vorbis. */
36659 (void)pDecoder;
36660 return 0;
36661}
36662
36663static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
36664{
36665 stb_vorbis* pInternalVorbis = NULL;
36666 size_t dataSize = 0;
36667 size_t dataCapacity = 0;
36668 ma_uint8* pData = NULL;
36669 stb_vorbis_info vorbisInfo;
36670 size_t vorbisDataSize;
36671 ma_vorbis_decoder* pVorbis;
36672
36673 MA_ASSERT(pConfig != NULL);
36674 MA_ASSERT(pDecoder != NULL);
36675
36676 /* We grow the buffer in chunks. */
36677 do
36678 {
36679 /* Allocate memory for a new chunk. */
36680 ma_uint8* pNewData;
36681 size_t bytesRead;
36682 int vorbisError = 0;
36683 int consumedDataSize = 0;
36684 size_t oldCapacity = dataCapacity;
36685
36686 dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE;
36687 pNewData = (ma_uint8*)ma__realloc_from_callbacks(pData, dataCapacity, oldCapacity, &pDecoder->allocationCallbacks);
36688 if (pNewData == NULL) {
36689 ma__free_from_callbacks(pData, &pDecoder->allocationCallbacks);
36690 return MA_OUT_OF_MEMORY;
36691 }
36692
36693 pData = pNewData;
36694
36695 /* Fill in a chunk. */
36696 bytesRead = ma_decoder_read_bytes(pDecoder, pData + dataSize, (dataCapacity - dataSize));
36697 if (bytesRead == 0) {
36698 return MA_ERROR;
36699 }
36700
36701 dataSize += bytesRead;
36702 if (dataSize > INT_MAX) {
36703 return MA_ERROR; /* Too big. */
36704 }
36705
36706 pInternalVorbis = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL);
36707 if (pInternalVorbis != NULL) {
36708 /*
36709 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
36710 we need to move those bytes down to the front of the buffer since they'll be needed for future decoding.
36711 */
36712 size_t leftoverDataSize = (dataSize - (size_t)consumedDataSize);
36713 size_t i;
36714 for (i = 0; i < leftoverDataSize; ++i) {
36715 pData[i] = pData[i + consumedDataSize];
36716 }
36717
36718 dataSize = leftoverDataSize;
36719 break; /* Success. */
36720 } else {
36721 if (vorbisError == VORBIS_need_more_data) {
36722 continue;
36723 } else {
36724 return MA_ERROR; /* Failed to open the stb_vorbis decoder. */
36725 }
36726 }
36727 } while (MA_TRUE);
36728
36729
36730 /* If we get here it means we successfully opened the Vorbis decoder. */
36731 vorbisInfo = stb_vorbis_get_info(pInternalVorbis);
36732
36733 /* Don't allow more than MA_MAX_CHANNELS channels. */
36734 if (vorbisInfo.channels > MA_MAX_CHANNELS) {
36735 stb_vorbis_close(pInternalVorbis);
36736 ma__free_from_callbacks(pData, &pDecoder->allocationCallbacks);
36737 return MA_ERROR; /* Too many channels. */
36738 }
36739
36740 vorbisDataSize = sizeof(ma_vorbis_decoder) + sizeof(float)*vorbisInfo.max_frame_size;
36741 pVorbis = (ma_vorbis_decoder*)ma__malloc_from_callbacks(vorbisDataSize, &pDecoder->allocationCallbacks);
36742 if (pVorbis == NULL) {
36743 stb_vorbis_close(pInternalVorbis);
36744 ma__free_from_callbacks(pData, &pDecoder->allocationCallbacks);
36745 return MA_OUT_OF_MEMORY;
36746 }
36747
36748 MA_ZERO_MEMORY(pVorbis, vorbisDataSize);
36749 pVorbis->pInternalVorbis = pInternalVorbis;
36750 pVorbis->pData = pData;
36751 pVorbis->dataSize = dataSize;
36752 pVorbis->dataCapacity = dataCapacity;
36753
36754 pDecoder->onReadPCMFrames = ma_decoder_internal_on_read_pcm_frames__vorbis;
36755 pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__vorbis;
36756 pDecoder->onUninit = ma_decoder_internal_on_uninit__vorbis;
36757 pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__vorbis;
36758 pDecoder->pInternalDecoder = pVorbis;
36759
36760 /* The internal format is always f32. */
36761 pDecoder->internalFormat = ma_format_f32;
36762 pDecoder->internalChannels = vorbisInfo.channels;
36763 pDecoder->internalSampleRate = vorbisInfo.sample_rate;
36764 ma_get_standard_channel_map(ma_standard_channel_map_vorbis, pDecoder->internalChannels, pDecoder->internalChannelMap);
36765
36766 return MA_SUCCESS;
36767}
36768#endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */
36769
36770/* MP3 */
36771#ifdef dr_mp3_h
36772#define MA_HAS_MP3
36773
36774static size_t ma_decoder_internal_on_read__mp3(void* pUserData, void* pBufferOut, size_t bytesToRead)
36775{
36776 ma_decoder* pDecoder = (ma_decoder*)pUserData;
36777 MA_ASSERT(pDecoder != NULL);
36778
36779 return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead);
36780}
36781
36782static drmp3_bool32 ma_decoder_internal_on_seek__mp3(void* pUserData, int offset, drmp3_seek_origin origin)
36783{
36784 ma_decoder* pDecoder = (ma_decoder*)pUserData;
36785 MA_ASSERT(pDecoder != NULL);
36786
36787 return ma_decoder_seek_bytes(pDecoder, offset, (origin == drmp3_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
36788}
36789
36790static ma_uint64 ma_decoder_internal_on_read_pcm_frames__mp3(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
36791{
36792 drmp3* pMP3;
36793
36794 MA_ASSERT(pDecoder != NULL);
36795 MA_ASSERT(pFramesOut != NULL);
36796 MA_ASSERT(pDecoder->internalFormat == ma_format_f32);
36797
36798 pMP3 = (drmp3*)pDecoder->pInternalDecoder;
36799 MA_ASSERT(pMP3 != NULL);
36800
36801 return drmp3_read_pcm_frames_f32(pMP3, frameCount, (float*)pFramesOut);
36802}
36803
36804static ma_result ma_decoder_internal_on_seek_to_pcm_frame__mp3(ma_decoder* pDecoder, ma_uint64 frameIndex)
36805{
36806 drmp3* pMP3;
36807 drmp3_bool32 result;
36808
36809 pMP3 = (drmp3*)pDecoder->pInternalDecoder;
36810 MA_ASSERT(pMP3 != NULL);
36811
36812 result = drmp3_seek_to_pcm_frame(pMP3, frameIndex);
36813 if (result) {
36814 return MA_SUCCESS;
36815 } else {
36816 return MA_ERROR;
36817 }
36818}
36819
36820static ma_result ma_decoder_internal_on_uninit__mp3(ma_decoder* pDecoder)
36821{
36822 drmp3_uninit((drmp3*)pDecoder->pInternalDecoder);
36823 ma__free_from_callbacks(pDecoder->pInternalDecoder, &pDecoder->allocationCallbacks);
36824 return MA_SUCCESS;
36825}
36826
36827static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__mp3(ma_decoder* pDecoder)
36828{
36829 return drmp3_get_pcm_frame_count((drmp3*)pDecoder->pInternalDecoder);
36830}
36831
36832static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
36833{
36834 drmp3* pMP3;
36835 drmp3_config mp3Config;
36836 drmp3_allocation_callbacks allocationCallbacks;
36837
36838 MA_ASSERT(pConfig != NULL);
36839 MA_ASSERT(pDecoder != NULL);
36840
36841 pMP3 = (drmp3*)ma__malloc_from_callbacks(sizeof(*pMP3), &pDecoder->allocationCallbacks);
36842 if (pMP3 == NULL) {
36843 return MA_OUT_OF_MEMORY;
36844 }
36845
36846 allocationCallbacks.pUserData = pDecoder->allocationCallbacks.pUserData;
36847 allocationCallbacks.onMalloc = pDecoder->allocationCallbacks.onMalloc;
36848 allocationCallbacks.onRealloc = pDecoder->allocationCallbacks.onRealloc;
36849 allocationCallbacks.onFree = pDecoder->allocationCallbacks.onFree;
36850
36851 /*
36852 Try opening the decoder first. MP3 can have variable sample rates (it's per frame/packet). We therefore need
36853 to use some smarts to determine the most appropriate internal sample rate. These are the rules we're going
36854 to use:
36855
36856 Sample Rates
36857 1) If an output sample rate is specified in pConfig we just use that. Otherwise;
36858 2) Fall back to 44100.
36859
36860 The internal channel count is always stereo, and the internal format is always f32.
36861 */
36862 MA_ZERO_OBJECT(&mp3Config);
36863 mp3Config.outputChannels = 2;
36864 mp3Config.outputSampleRate = (pConfig->sampleRate != 0) ? pConfig->sampleRate : 44100;
36865 if (!drmp3_init(pMP3, ma_decoder_internal_on_read__mp3, ma_decoder_internal_on_seek__mp3, pDecoder, &mp3Config, &allocationCallbacks)) {
36866 ma__free_from_callbacks(pMP3, &pDecoder->allocationCallbacks);
36867 return MA_ERROR;
36868 }
36869
36870 /* If we get here it means we successfully initialized the MP3 decoder. We can now initialize the rest of the ma_decoder. */
36871 pDecoder->onReadPCMFrames = ma_decoder_internal_on_read_pcm_frames__mp3;
36872 pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__mp3;
36873 pDecoder->onUninit = ma_decoder_internal_on_uninit__mp3;
36874 pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__mp3;
36875 pDecoder->pInternalDecoder = pMP3;
36876
36877 /* Internal format. */
36878 pDecoder->internalFormat = ma_format_f32;
36879 pDecoder->internalChannels = pMP3->channels;
36880 pDecoder->internalSampleRate = pMP3->sampleRate;
36881 ma_get_standard_channel_map(ma_standard_channel_map_default, pDecoder->internalChannels, pDecoder->internalChannelMap);
36882
36883 return MA_SUCCESS;
36884}
36885#endif /* dr_mp3_h */
36886
36887/* Raw */
36888static ma_uint64 ma_decoder_internal_on_read_pcm_frames__raw(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
36889{
36890 ma_uint32 bpf;
36891 ma_uint64 totalFramesRead;
36892 void* pRunningFramesOut;
36893
36894
36895 MA_ASSERT(pDecoder != NULL);
36896 MA_ASSERT(pFramesOut != NULL);
36897
36898 /* For raw decoding we just read directly from the decoder's callbacks. */
36899 bpf = ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
36900
36901 totalFramesRead = 0;
36902 pRunningFramesOut = pFramesOut;
36903
36904 while (totalFramesRead < frameCount) {
36905 ma_uint64 framesReadThisIteration;
36906 ma_uint64 framesToReadThisIteration = (frameCount - totalFramesRead);
36907 if (framesToReadThisIteration > MA_SIZE_MAX) {
36908 framesToReadThisIteration = MA_SIZE_MAX;
36909 }
36910
36911 framesReadThisIteration = ma_decoder_read_bytes(pDecoder, pRunningFramesOut, (size_t)framesToReadThisIteration * bpf) / bpf; /* Safe cast to size_t. */
36912
36913 totalFramesRead += framesReadThisIteration;
36914 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIteration * bpf);
36915
36916 if (framesReadThisIteration < framesToReadThisIteration) {
36917 break; /* Done. */
36918 }
36919 }
36920
36921 return totalFramesRead;
36922}
36923
36924static ma_result ma_decoder_internal_on_seek_to_pcm_frame__raw(ma_decoder* pDecoder, ma_uint64 frameIndex)
36925{
36926 ma_bool32 result = MA_FALSE;
36927 ma_uint64 totalBytesToSeek;
36928
36929 MA_ASSERT(pDecoder != NULL);
36930
36931 if (pDecoder->onSeek == NULL) {
36932 return MA_ERROR;
36933 }
36934
36935 /* 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. */
36936 totalBytesToSeek = frameIndex * ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
36937 if (totalBytesToSeek < 0x7FFFFFFF) {
36938 /* Simple case. */
36939 result = ma_decoder_seek_bytes(pDecoder, (int)(frameIndex * ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels)), ma_seek_origin_start);
36940 } else {
36941 /* Complex case. Start by doing a seek relative to the start. Then keep looping using offset seeking. */
36942 result = ma_decoder_seek_bytes(pDecoder, 0x7FFFFFFF, ma_seek_origin_start);
36943 if (result == MA_TRUE) {
36944 totalBytesToSeek -= 0x7FFFFFFF;
36945
36946 while (totalBytesToSeek > 0) {
36947 ma_uint64 bytesToSeekThisIteration = totalBytesToSeek;
36948 if (bytesToSeekThisIteration > 0x7FFFFFFF) {
36949 bytesToSeekThisIteration = 0x7FFFFFFF;
36950 }
36951
36952 result = ma_decoder_seek_bytes(pDecoder, (int)bytesToSeekThisIteration, ma_seek_origin_current);
36953 if (result != MA_TRUE) {
36954 break;
36955 }
36956
36957 totalBytesToSeek -= bytesToSeekThisIteration;
36958 }
36959 }
36960 }
36961
36962 if (result) {
36963 return MA_SUCCESS;
36964 } else {
36965 return MA_ERROR;
36966 }
36967}
36968
36969static ma_result ma_decoder_internal_on_uninit__raw(ma_decoder* pDecoder)
36970{
36971 (void)pDecoder;
36972 return MA_SUCCESS;
36973}
36974
36975static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__raw(ma_decoder* pDecoder)
36976{
36977 (void)pDecoder;
36978 return 0;
36979}
36980
36981static ma_result ma_decoder_init_raw__internal(const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder)
36982{
36983 MA_ASSERT(pConfigIn != NULL);
36984 MA_ASSERT(pConfigOut != NULL);
36985 MA_ASSERT(pDecoder != NULL);
36986
36987 pDecoder->onReadPCMFrames = ma_decoder_internal_on_read_pcm_frames__raw;
36988 pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__raw;
36989 pDecoder->onUninit = ma_decoder_internal_on_uninit__raw;
36990 pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__raw;
36991
36992 /* Internal format. */
36993 pDecoder->internalFormat = pConfigIn->format;
36994 pDecoder->internalChannels = pConfigIn->channels;
36995 pDecoder->internalSampleRate = pConfigIn->sampleRate;
36996 ma_channel_map_copy(pDecoder->internalChannelMap, pConfigIn->channelMap, pConfigIn->channels);
36997
36998 return MA_SUCCESS;
36999}
37000
37001static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37002{
37003 MA_ASSERT(pDecoder != NULL);
37004
37005 if (pConfig != NULL) {
37006 return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks);
37007 } else {
37008 pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default();
37009 return MA_SUCCESS;
37010 }
37011}
37012
37013static 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)
37014{
37015 ma_result result;
37016
37017 MA_ASSERT(pConfig != NULL);
37018
37019 if (pDecoder == NULL) {
37020 return MA_INVALID_ARGS;
37021 }
37022
37023 MA_ZERO_OBJECT(pDecoder);
37024
37025 if (onRead == NULL || onSeek == NULL) {
37026 return MA_INVALID_ARGS;
37027 }
37028
37029 pDecoder->onRead = onRead;
37030 pDecoder->onSeek = onSeek;
37031 pDecoder->pUserData = pUserData;
37032
37033 result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder);
37034 if (result != MA_SUCCESS) {
37035 return result;
37036 }
37037
37038 return MA_SUCCESS;
37039}
37040
37041static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37042{
37043 ma_result result;
37044
37045 result = ma_decoder__init_data_converter(pDecoder, pConfig);
37046 if (result != MA_SUCCESS) {
37047 return result;
37048 }
37049
37050 return result;
37051}
37052
37053ma_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)
37054{
37055 ma_decoder_config config;
37056 ma_result result;
37057
37058 config = ma_decoder_config_init_copy(pConfig);
37059
37060 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
37061 if (result != MA_SUCCESS) {
37062 return result;
37063 }
37064
37065#ifdef MA_HAS_WAV
37066 result = ma_decoder_init_wav__internal(&config, pDecoder);
37067#else
37068 result = MA_NO_BACKEND;
37069#endif
37070 if (result != MA_SUCCESS) {
37071 return result;
37072 }
37073
37074 return ma_decoder__postinit(&config, pDecoder);
37075}
37076
37077ma_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)
37078{
37079 ma_decoder_config config;
37080 ma_result result;
37081
37082 config = ma_decoder_config_init_copy(pConfig);
37083
37084 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
37085 if (result != MA_SUCCESS) {
37086 return result;
37087 }
37088
37089#ifdef MA_HAS_FLAC
37090 result = ma_decoder_init_flac__internal(&config, pDecoder);
37091#else
37092 result = MA_NO_BACKEND;
37093#endif
37094 if (result != MA_SUCCESS) {
37095 return result;
37096 }
37097
37098 return ma_decoder__postinit(&config, pDecoder);
37099}
37100
37101ma_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)
37102{
37103 ma_decoder_config config;
37104 ma_result result;
37105
37106 config = ma_decoder_config_init_copy(pConfig);
37107
37108 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
37109 if (result != MA_SUCCESS) {
37110 return result;
37111 }
37112
37113#ifdef MA_HAS_VORBIS
37114 result = ma_decoder_init_vorbis__internal(&config, pDecoder);
37115#else
37116 result = MA_NO_BACKEND;
37117#endif
37118 if (result != MA_SUCCESS) {
37119 return result;
37120 }
37121
37122 return ma_decoder__postinit(&config, pDecoder);
37123}
37124
37125ma_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)
37126{
37127 ma_decoder_config config;
37128 ma_result result;
37129
37130 config = ma_decoder_config_init_copy(pConfig);
37131
37132 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
37133 if (result != MA_SUCCESS) {
37134 return result;
37135 }
37136
37137#ifdef MA_HAS_MP3
37138 result = ma_decoder_init_mp3__internal(&config, pDecoder);
37139#else
37140 result = MA_NO_BACKEND;
37141#endif
37142 if (result != MA_SUCCESS) {
37143 return result;
37144 }
37145
37146 return ma_decoder__postinit(&config, pDecoder);
37147}
37148
37149ma_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)
37150{
37151 ma_decoder_config config;
37152 ma_result result;
37153
37154 config = ma_decoder_config_init_copy(pConfigOut);
37155
37156 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
37157 if (result != MA_SUCCESS) {
37158 return result;
37159 }
37160
37161 result = ma_decoder_init_raw__internal(pConfigIn, &config, pDecoder);
37162 if (result != MA_SUCCESS) {
37163 return result;
37164 }
37165
37166 return ma_decoder__postinit(&config, pDecoder);
37167}
37168
37169static 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)
37170{
37171 ma_result result = MA_NO_BACKEND;
37172
37173 MA_ASSERT(pConfig != NULL);
37174 MA_ASSERT(pDecoder != NULL);
37175
37176 /* Silence some warnings in the case that we don't have any decoder backends enabled. */
37177 (void)onRead;
37178 (void)onSeek;
37179 (void)pUserData;
37180 (void)pConfig;
37181 (void)pDecoder;
37182
37183 /* We use trial and error to open a decoder. */
37184
37185#ifdef MA_HAS_WAV
37186 if (result != MA_SUCCESS) {
37187 result = ma_decoder_init_wav__internal(pConfig, pDecoder);
37188 if (result != MA_SUCCESS) {
37189 onSeek(pDecoder, 0, ma_seek_origin_start);
37190 }
37191 }
37192#endif
37193#ifdef MA_HAS_FLAC
37194 if (result != MA_SUCCESS) {
37195 result = ma_decoder_init_flac__internal(pConfig, pDecoder);
37196 if (result != MA_SUCCESS) {
37197 onSeek(pDecoder, 0, ma_seek_origin_start);
37198 }
37199 }
37200#endif
37201#ifdef MA_HAS_VORBIS
37202 if (result != MA_SUCCESS) {
37203 result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);
37204 if (result != MA_SUCCESS) {
37205 onSeek(pDecoder, 0, ma_seek_origin_start);
37206 }
37207 }
37208#endif
37209#ifdef MA_HAS_MP3
37210 if (result != MA_SUCCESS) {
37211 result = ma_decoder_init_mp3__internal(pConfig, pDecoder);
37212 if (result != MA_SUCCESS) {
37213 onSeek(pDecoder, 0, ma_seek_origin_start);
37214 }
37215 }
37216#endif
37217
37218 if (result != MA_SUCCESS) {
37219 return result;
37220 }
37221
37222 return ma_decoder__postinit(pConfig, pDecoder);
37223}
37224
37225ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37226{
37227 ma_decoder_config config;
37228 ma_result result;
37229
37230 config = ma_decoder_config_init_copy(pConfig);
37231
37232 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
37233 if (result != MA_SUCCESS) {
37234 return result;
37235 }
37236
37237 return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder);
37238}
37239
37240
37241static size_t ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
37242{
37243 size_t bytesRemaining;
37244
37245 MA_ASSERT(pDecoder->memory.dataSize >= pDecoder->memory.currentReadPos);
37246
37247 bytesRemaining = pDecoder->memory.dataSize - pDecoder->memory.currentReadPos;
37248 if (bytesToRead > bytesRemaining) {
37249 bytesToRead = bytesRemaining;
37250 }
37251
37252 if (bytesToRead > 0) {
37253 MA_COPY_MEMORY(pBufferOut, pDecoder->memory.pData + pDecoder->memory.currentReadPos, bytesToRead);
37254 pDecoder->memory.currentReadPos += bytesToRead;
37255 }
37256
37257 return bytesToRead;
37258}
37259
37260static ma_bool32 ma_decoder__on_seek_memory(ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin)
37261{
37262 if (origin == ma_seek_origin_current) {
37263 if (byteOffset > 0) {
37264 if (pDecoder->memory.currentReadPos + byteOffset > pDecoder->memory.dataSize) {
37265 byteOffset = (int)(pDecoder->memory.dataSize - pDecoder->memory.currentReadPos); /* Trying to seek too far forward. */
37266 }
37267 } else {
37268 if (pDecoder->memory.currentReadPos < (size_t)-byteOffset) {
37269 byteOffset = -(int)pDecoder->memory.currentReadPos; /* Trying to seek too far backwards. */
37270 }
37271 }
37272
37273 /* This will never underflow thanks to the clamps above. */
37274 pDecoder->memory.currentReadPos += byteOffset;
37275 } else {
37276 if ((ma_uint32)byteOffset <= pDecoder->memory.dataSize) {
37277 pDecoder->memory.currentReadPos = byteOffset;
37278 } else {
37279 pDecoder->memory.currentReadPos = pDecoder->memory.dataSize; /* Trying to seek too far forward. */
37280 }
37281 }
37282
37283 return MA_TRUE;
37284}
37285
37286static ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37287{
37288 ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, pConfig, pDecoder);
37289 if (result != MA_SUCCESS) {
37290 return result;
37291 }
37292
37293 if (pData == NULL || dataSize == 0) {
37294 return MA_INVALID_ARGS;
37295 }
37296
37297 pDecoder->memory.pData = (const ma_uint8*)pData;
37298 pDecoder->memory.dataSize = dataSize;
37299 pDecoder->memory.currentReadPos = 0;
37300
37301 (void)pConfig;
37302 return MA_SUCCESS;
37303}
37304
37305ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37306{
37307 ma_decoder_config config;
37308 ma_result result;
37309
37310 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
37311
37312 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
37313 if (result != MA_SUCCESS) {
37314 return result;
37315 }
37316
37317 return ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder);
37318}
37319
37320ma_result ma_decoder_init_memory_wav(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37321{
37322 ma_decoder_config config;
37323 ma_result result;
37324
37325 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
37326
37327 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
37328 if (result != MA_SUCCESS) {
37329 return result;
37330 }
37331
37332#ifdef MA_HAS_WAV
37333 result = ma_decoder_init_wav__internal(&config, pDecoder);
37334#else
37335 result = MA_NO_BACKEND;
37336#endif
37337 if (result != MA_SUCCESS) {
37338 return result;
37339 }
37340
37341 return ma_decoder__postinit(&config, pDecoder);
37342}
37343
37344ma_result ma_decoder_init_memory_flac(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37345{
37346 ma_decoder_config config;
37347 ma_result result;
37348
37349 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
37350
37351 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
37352 if (result != MA_SUCCESS) {
37353 return result;
37354 }
37355
37356#ifdef MA_HAS_FLAC
37357 result = ma_decoder_init_flac__internal(&config, pDecoder);
37358#else
37359 result = MA_NO_BACKEND;
37360#endif
37361 if (result != MA_SUCCESS) {
37362 return result;
37363 }
37364
37365 return ma_decoder__postinit(&config, pDecoder);
37366}
37367
37368ma_result ma_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37369{
37370 ma_decoder_config config;
37371 ma_result result;
37372
37373 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
37374
37375 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
37376 if (result != MA_SUCCESS) {
37377 return result;
37378 }
37379
37380#ifdef MA_HAS_VORBIS
37381 result = ma_decoder_init_vorbis__internal(&config, pDecoder);
37382#else
37383 result = MA_NO_BACKEND;
37384#endif
37385 if (result != MA_SUCCESS) {
37386 return result;
37387 }
37388
37389 return ma_decoder__postinit(&config, pDecoder);
37390}
37391
37392ma_result ma_decoder_init_memory_mp3(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37393{
37394 ma_decoder_config config;
37395 ma_result result;
37396
37397 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
37398
37399 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
37400 if (result != MA_SUCCESS) {
37401 return result;
37402 }
37403
37404#ifdef MA_HAS_MP3
37405 result = ma_decoder_init_mp3__internal(&config, pDecoder);
37406#else
37407 result = MA_NO_BACKEND;
37408#endif
37409 if (result != MA_SUCCESS) {
37410 return result;
37411 }
37412
37413 return ma_decoder__postinit(&config, pDecoder);
37414}
37415
37416ma_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)
37417{
37418 ma_decoder_config config;
37419 ma_result result;
37420
37421 config = ma_decoder_config_init_copy(pConfigOut); /* Make sure the config is not NULL. */
37422
37423 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
37424 if (result != MA_SUCCESS) {
37425 return result;
37426 }
37427
37428 result = ma_decoder_init_raw__internal(pConfigIn, &config, pDecoder);
37429 if (result != MA_SUCCESS) {
37430 return result;
37431 }
37432
37433 return ma_decoder__postinit(&config, pDecoder);
37434}
37435
37436#ifndef MA_NO_STDIO
37437static const char* ma_path_file_name(const char* path)
37438{
37439 const char* fileName;
37440
37441 if (path == NULL) {
37442 return NULL;
37443 }
37444
37445 fileName = path;
37446
37447 /* We just loop through the path until we find the last slash. */
37448 while (path[0] != '\0') {
37449 if (path[0] == '/' || path[0] == '\\') {
37450 fileName = path;
37451 }
37452
37453 path += 1;
37454 }
37455
37456 /* At this point the file name is sitting on a slash, so just move forward. */
37457 while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
37458 fileName += 1;
37459 }
37460
37461 return fileName;
37462}
37463
37464static const wchar_t* ma_path_file_name_w(const wchar_t* path)
37465{
37466 const wchar_t* fileName;
37467
37468 if (path == NULL) {
37469 return NULL;
37470 }
37471
37472 fileName = path;
37473
37474 /* We just loop through the path until we find the last slash. */
37475 while (path[0] != '\0') {
37476 if (path[0] == '/' || path[0] == '\\') {
37477 fileName = path;
37478 }
37479
37480 path += 1;
37481 }
37482
37483 /* At this point the file name is sitting on a slash, so just move forward. */
37484 while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
37485 fileName += 1;
37486 }
37487
37488 return fileName;
37489}
37490
37491
37492static const char* ma_path_extension(const char* path)
37493{
37494 const char* extension;
37495 const char* lastOccurance;
37496
37497 if (path == NULL) {
37498 path = "";
37499 }
37500
37501 extension = ma_path_file_name(path);
37502 lastOccurance = NULL;
37503
37504 /* Just find the last '.' and return. */
37505 while (extension[0] != '\0') {
37506 if (extension[0] == '.') {
37507 extension += 1;
37508 lastOccurance = extension;
37509 }
37510
37511 extension += 1;
37512 }
37513
37514 return (lastOccurance != NULL) ? lastOccurance : extension;
37515}
37516
37517static const wchar_t* ma_path_extension_w(const wchar_t* path)
37518{
37519 const wchar_t* extension;
37520 const wchar_t* lastOccurance;
37521
37522 if (path == NULL) {
37523 path = L"";
37524 }
37525
37526 extension = ma_path_file_name_w(path);
37527 lastOccurance = NULL;
37528
37529 /* Just find the last '.' and return. */
37530 while (extension[0] != '\0') {
37531 if (extension[0] == '.') {
37532 extension += 1;
37533 lastOccurance = extension;
37534 }
37535
37536 extension += 1;
37537 }
37538
37539 return (lastOccurance != NULL) ? lastOccurance : extension;
37540}
37541
37542
37543static ma_bool32 ma_path_extension_equal(const char* path, const char* extension)
37544{
37545 const char* ext1;
37546 const char* ext2;
37547
37548 if (path == NULL || extension == NULL) {
37549 return MA_FALSE;
37550 }
37551
37552 ext1 = extension;
37553 ext2 = ma_path_extension(path);
37554
37555#if defined(_MSC_VER) || defined(__DMC__)
37556 return _stricmp(ext1, ext2) == 0;
37557#else
37558 return strcasecmp(ext1, ext2) == 0;
37559#endif
37560}
37561
37562static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension)
37563{
37564 const wchar_t* ext1;
37565 const wchar_t* ext2;
37566
37567 if (path == NULL || extension == NULL) {
37568 return MA_FALSE;
37569 }
37570
37571 ext1 = extension;
37572 ext2 = ma_path_extension_w(path);
37573
37574#if defined(_MSC_VER) || defined(__DMC__)
37575 return _wcsicmp(ext1, ext2) == 0;
37576#else
37577 /*
37578 I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This
37579 isn't the most efficient way to do it, but it should work OK.
37580 */
37581 {
37582 char ext1MB[4096];
37583 char ext2MB[4096];
37584 const wchar_t* pext1 = ext1;
37585 const wchar_t* pext2 = ext2;
37586 mbstate_t mbs1;
37587 mbstate_t mbs2;
37588
37589 MA_ZERO_OBJECT(&mbs1);
37590 MA_ZERO_OBJECT(&mbs2);
37591
37592 if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) {
37593 return MA_FALSE;
37594 }
37595 if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) {
37596 return MA_FALSE;
37597 }
37598
37599 return strcasecmp(ext1MB, ext2MB) == 0;
37600 }
37601#endif
37602}
37603
37604
37605static size_t ma_decoder__on_read_stdio(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
37606{
37607 return fread(pBufferOut, 1, bytesToRead, (FILE*)pDecoder->pUserData);
37608}
37609
37610static ma_bool32 ma_decoder__on_seek_stdio(ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin)
37611{
37612 return fseek((FILE*)pDecoder->pUserData, byteOffset, (origin == ma_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
37613}
37614
37615static ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37616{
37617 ma_result result;
37618 FILE* pFile;
37619
37620 if (pDecoder == NULL) {
37621 return MA_INVALID_ARGS;
37622 }
37623
37624 MA_ZERO_OBJECT(pDecoder);
37625
37626 if (pFilePath == NULL || pFilePath[0] == '\0') {
37627 return MA_INVALID_ARGS;
37628 }
37629
37630 result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder);
37631 if (result != MA_SUCCESS) {
37632 return result;
37633 }
37634
37635#if defined(_MSC_VER) && _MSC_VER >= 1400
37636 if (fopen_s(&pFile, pFilePath, "rb") != 0) {
37637 return MA_ERROR;
37638 }
37639#else
37640 pFile = fopen(pFilePath, "rb");
37641 if (pFile == NULL) {
37642 return MA_ERROR;
37643 }
37644#endif
37645
37646 /* We need to manually set the user data so the calls to ma_decoder__on_seek_stdio() succeed. */
37647 pDecoder->pUserData = pFile;
37648
37649 return MA_SUCCESS;
37650}
37651
37652/*
37653_wfopen() isn't always available in all compilation environments.
37654
37655 * Windows only.
37656 * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back).
37657 * MinGW-64 (both 32- and 64-bit) seems to support it.
37658 * MinGW wraps it in !defined(__STRICT_ANSI__).
37659
37660This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs()
37661fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support.
37662*/
37663#if defined(_WIN32)
37664 #if defined(_MSC_VER) || defined(__MINGW64__) || !defined(__STRICT_ANSI__)
37665 #define MA_HAS_WFOPEN
37666 #endif
37667#endif
37668
37669static ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37670{
37671 ma_result result;
37672 FILE* pFile;
37673
37674 if (pDecoder == NULL) {
37675 return MA_INVALID_ARGS;
37676 }
37677
37678 MA_ZERO_OBJECT(pDecoder);
37679
37680 if (pFilePath == NULL || pFilePath[0] == '\0') {
37681 return MA_INVALID_ARGS;
37682 }
37683
37684 result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder);
37685 if (result != MA_SUCCESS) {
37686 return result;
37687 }
37688
37689#if defined(MA_HAS_WFOPEN)
37690 /* Use _wfopen() on Windows. */
37691 #if defined(_MSC_VER) && _MSC_VER >= 1400
37692 if (_wfopen_s(&pFile, pFilePath, L"rb") != 0) {
37693 return MA_ERROR;
37694 }
37695 #else
37696 pFile = _wfopen(pFilePath, L"rb");
37697 if (pFile == NULL) {
37698 return MA_ERROR;
37699 }
37700 #endif
37701#else
37702 /*
37703 Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can
37704 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
37705 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.
37706 */
37707 {
37708 mbstate_t mbs;
37709 size_t lenMB;
37710 const wchar_t* pFilePathTemp = pFilePath;
37711 char* pFilePathMB = NULL;
37712
37713 /* Get the length first. */
37714 MA_ZERO_OBJECT(&mbs);
37715 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
37716 if (lenMB == (size_t)-1) {
37717 return MA_ERROR;
37718 }
37719
37720 pFilePathMB = (char*)ma__malloc_from_callbacks(lenMB + 1, &pDecoder->allocationCallbacks);
37721 if (pFilePathMB == NULL) {
37722 return MA_OUT_OF_MEMORY;
37723 }
37724
37725 pFilePathTemp = pFilePath;
37726 MA_ZERO_OBJECT(&mbs);
37727 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
37728
37729 pFile = fopen(pFilePathMB, "rb");
37730
37731 ma__free_from_callbacks(pFilePathMB, &pDecoder->allocationCallbacks);
37732 }
37733
37734 if (pFile == NULL) {
37735 return MA_ERROR;
37736 }
37737#endif
37738
37739 /* We need to manually set the user data so the calls to ma_decoder__on_seek_stdio() succeed. */
37740 pDecoder->pUserData = pFile;
37741
37742 (void)pConfig;
37743 return MA_SUCCESS;
37744}
37745
37746ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37747{
37748 ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder); /* This sets pDecoder->pUserData to a FILE*. */
37749 if (result != MA_SUCCESS) {
37750 return result;
37751 }
37752
37753 /* WAV */
37754 if (ma_path_extension_equal(pFilePath, "wav")) {
37755 result = ma_decoder_init_wav(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
37756 if (result == MA_SUCCESS) {
37757 return MA_SUCCESS;
37758 }
37759
37760 ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
37761 }
37762
37763 /* FLAC */
37764 if (ma_path_extension_equal(pFilePath, "flac")) {
37765 result = ma_decoder_init_flac(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
37766 if (result == MA_SUCCESS) {
37767 return MA_SUCCESS;
37768 }
37769
37770 ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
37771 }
37772
37773 /* MP3 */
37774 if (ma_path_extension_equal(pFilePath, "mp3")) {
37775 result = ma_decoder_init_mp3(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
37776 if (result == MA_SUCCESS) {
37777 return MA_SUCCESS;
37778 }
37779
37780 ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
37781 }
37782
37783 /* Trial and error. */
37784 return ma_decoder_init(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
37785}
37786
37787ma_result ma_decoder_init_file_wav(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37788{
37789 ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder);
37790 if (result != MA_SUCCESS) {
37791 return result;
37792 }
37793
37794 return ma_decoder_init_wav(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
37795}
37796
37797ma_result ma_decoder_init_file_flac(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37798{
37799 ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder);
37800 if (result != MA_SUCCESS) {
37801 return result;
37802 }
37803
37804 return ma_decoder_init_flac(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
37805}
37806
37807ma_result ma_decoder_init_file_vorbis(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37808{
37809 ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder);
37810 if (result != MA_SUCCESS) {
37811 return result;
37812 }
37813
37814 return ma_decoder_init_vorbis(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
37815}
37816
37817ma_result ma_decoder_init_file_mp3(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37818{
37819 ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder);
37820 if (result != MA_SUCCESS) {
37821 return result;
37822 }
37823
37824 return ma_decoder_init_mp3(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
37825}
37826
37827
37828ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37829{
37830 ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder); /* This sets pDecoder->pUserData to a FILE*. */
37831 if (result != MA_SUCCESS) {
37832 return result;
37833 }
37834
37835 /* WAV */
37836 if (ma_path_extension_equal_w(pFilePath, L"wav")) {
37837 result = ma_decoder_init_wav(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
37838 if (result == MA_SUCCESS) {
37839 return MA_SUCCESS;
37840 }
37841
37842 ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
37843 }
37844
37845 /* FLAC */
37846 if (ma_path_extension_equal_w(pFilePath, L"flac")) {
37847 result = ma_decoder_init_flac(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
37848 if (result == MA_SUCCESS) {
37849 return MA_SUCCESS;
37850 }
37851
37852 ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
37853 }
37854
37855 /* MP3 */
37856 if (ma_path_extension_equal_w(pFilePath, L"mp3")) {
37857 result = ma_decoder_init_mp3(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
37858 if (result == MA_SUCCESS) {
37859 return MA_SUCCESS;
37860 }
37861
37862 ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
37863 }
37864
37865 /* Trial and error. */
37866 return ma_decoder_init(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
37867}
37868
37869ma_result ma_decoder_init_file_wav_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37870{
37871 ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder);
37872 if (result != MA_SUCCESS) {
37873 return result;
37874 }
37875
37876 return ma_decoder_init_wav(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
37877}
37878
37879ma_result ma_decoder_init_file_flac_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37880{
37881 ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder);
37882 if (result != MA_SUCCESS) {
37883 return result;
37884 }
37885
37886 return ma_decoder_init_flac(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
37887}
37888
37889ma_result ma_decoder_init_file_vorbis_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37890{
37891 ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder);
37892 if (result != MA_SUCCESS) {
37893 return result;
37894 }
37895
37896 return ma_decoder_init_vorbis(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
37897}
37898
37899ma_result ma_decoder_init_file_mp3_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
37900{
37901 ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder);
37902 if (result != MA_SUCCESS) {
37903 return result;
37904 }
37905
37906 return ma_decoder_init_mp3(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
37907}
37908#endif /* MA_NO_STDIO */
37909
37910ma_result ma_decoder_uninit(ma_decoder* pDecoder)
37911{
37912 if (pDecoder == NULL) {
37913 return MA_INVALID_ARGS;
37914 }
37915
37916 if (pDecoder->onUninit) {
37917 pDecoder->onUninit(pDecoder);
37918 }
37919
37920#ifndef MA_NO_STDIO
37921 /* If we have a file handle, close it. */
37922 if (pDecoder->onRead == ma_decoder__on_read_stdio) {
37923 fclose((FILE*)pDecoder->pUserData);
37924 }
37925#endif
37926
37927 ma_data_converter_uninit(&pDecoder->converter);
37928
37929 return MA_SUCCESS;
37930}
37931
37932ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder)
37933{
37934 if (pDecoder == NULL) {
37935 return 0;
37936 }
37937
37938 if (pDecoder->onGetLengthInPCMFrames) {
37939 ma_uint64 nativeLengthInPCMFrames = pDecoder->onGetLengthInPCMFrames(pDecoder);
37940 if (pDecoder->internalSampleRate == pDecoder->outputSampleRate) {
37941 return nativeLengthInPCMFrames;
37942 } else {
37943 return ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, pDecoder->internalSampleRate, nativeLengthInPCMFrames);
37944 }
37945 }
37946
37947 return 0;
37948}
37949
37950ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
37951{
37952 ma_result result;
37953 ma_uint64 totalFramesReadOut;
37954 ma_uint64 totalFramesReadIn;
37955 void* pRunningFramesOut;
37956
37957 if (pDecoder == NULL) {
37958 return 0;
37959 }
37960
37961 if (pDecoder->onReadPCMFrames == NULL) {
37962 return 0;
37963 }
37964
37965 /* Fast path. */
37966 if (pDecoder->converter.isPassthrough) {
37967 return pDecoder->onReadPCMFrames(pDecoder, pFramesOut, frameCount);
37968 }
37969
37970 /* Getting here means we need to do data conversion. */
37971 totalFramesReadOut = 0;
37972 totalFramesReadIn = 0;
37973 pRunningFramesOut = pFramesOut;
37974
37975 while (totalFramesReadOut < frameCount) {
37976 ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */
37977 ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
37978 ma_uint64 framesToReadThisIterationIn;
37979 ma_uint64 framesReadThisIterationIn;
37980 ma_uint64 framesToReadThisIterationOut;
37981 ma_uint64 framesReadThisIterationOut;
37982 ma_uint64 requiredInputFrameCount;
37983
37984 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
37985 framesToReadThisIterationIn = framesToReadThisIterationOut;
37986 if (framesToReadThisIterationIn > intermediaryBufferCap) {
37987 framesToReadThisIterationIn = intermediaryBufferCap;
37988 }
37989
37990 requiredInputFrameCount = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut);
37991 if (framesToReadThisIterationIn > requiredInputFrameCount) {
37992 framesToReadThisIterationIn = requiredInputFrameCount;
37993 }
37994
37995 if (requiredInputFrameCount > 0) {
37996 framesReadThisIterationIn = pDecoder->onReadPCMFrames(pDecoder, pIntermediaryBuffer, framesToReadThisIterationIn);
37997 totalFramesReadIn += framesReadThisIterationIn;
37998 }
37999
38000 /*
38001 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
38002 input frames, we still want to try processing frames because there may some output frames generated from cached input data.
38003 */
38004 framesReadThisIterationOut = framesToReadThisIterationOut;
38005 result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
38006 if (result != MA_SUCCESS) {
38007 break;
38008 }
38009
38010 totalFramesReadOut += framesReadThisIterationOut;
38011 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
38012
38013 if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
38014 break; /* We're done. */
38015 }
38016 }
38017
38018 return totalFramesReadOut;
38019}
38020
38021ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex)
38022{
38023 if (pDecoder == NULL) {
38024 return 0;
38025 }
38026
38027 if (pDecoder->onSeekToPCMFrame) {
38028 return pDecoder->onSeekToPCMFrame(pDecoder, frameIndex);
38029 }
38030
38031 /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */
38032 return MA_INVALID_ARGS;
38033}
38034
38035
38036static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
38037{
38038 ma_uint64 totalFrameCount;
38039 ma_uint64 bpf;
38040 ma_uint64 dataCapInFrames;
38041 void* pPCMFramesOut;
38042
38043 MA_ASSERT(pDecoder != NULL);
38044
38045 totalFrameCount = 0;
38046 bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
38047
38048 /* The frame count is unknown until we try reading. Thus, we just run in a loop. */
38049 dataCapInFrames = 0;
38050 pPCMFramesOut = NULL;
38051 for (;;) {
38052 ma_uint64 frameCountToTryReading;
38053 ma_uint64 framesJustRead;
38054
38055 /* Make room if there's not enough. */
38056 if (totalFrameCount == dataCapInFrames) {
38057 void* pNewPCMFramesOut;
38058 ma_uint64 oldDataCapInFrames = dataCapInFrames;
38059 ma_uint64 newDataCapInFrames = dataCapInFrames*2;
38060 if (newDataCapInFrames == 0) {
38061 newDataCapInFrames = 4096;
38062 }
38063
38064 if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) {
38065 ma__free_from_callbacks(pPCMFramesOut, &pDecoder->allocationCallbacks);
38066 return MA_TOO_LARGE;
38067 }
38068
38069
38070 pNewPCMFramesOut = (void*)ma__realloc_from_callbacks(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), (size_t)(oldDataCapInFrames * bpf), &pDecoder->allocationCallbacks);
38071 if (pNewPCMFramesOut == NULL) {
38072 ma__free_from_callbacks(pPCMFramesOut, &pDecoder->allocationCallbacks);
38073 return MA_OUT_OF_MEMORY;
38074 }
38075
38076 dataCapInFrames = newDataCapInFrames;
38077 pPCMFramesOut = pNewPCMFramesOut;
38078 }
38079
38080 frameCountToTryReading = dataCapInFrames - totalFrameCount;
38081 MA_ASSERT(frameCountToTryReading > 0);
38082
38083 framesJustRead = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading);
38084 totalFrameCount += framesJustRead;
38085
38086 if (framesJustRead < frameCountToTryReading) {
38087 break;
38088 }
38089 }
38090
38091
38092 if (pConfigOut != NULL) {
38093 pConfigOut->format = pDecoder->outputFormat;
38094 pConfigOut->channels = pDecoder->outputChannels;
38095 pConfigOut->sampleRate = pDecoder->outputSampleRate;
38096 ma_channel_map_copy(pConfigOut->channelMap, pDecoder->outputChannelMap, pDecoder->outputChannels);
38097 }
38098
38099 if (ppPCMFramesOut != NULL) {
38100 *ppPCMFramesOut = pPCMFramesOut;
38101 } else {
38102 ma__free_from_callbacks(pPCMFramesOut, &pDecoder->allocationCallbacks);
38103 }
38104
38105 if (pFrameCountOut != NULL) {
38106 *pFrameCountOut = totalFrameCount;
38107 }
38108
38109 ma_decoder_uninit(pDecoder);
38110 return MA_SUCCESS;
38111}
38112
38113#ifndef MA_NO_STDIO
38114ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
38115{
38116 ma_decoder_config config;
38117 ma_decoder decoder;
38118 ma_result result;
38119
38120 if (pFrameCountOut != NULL) {
38121 *pFrameCountOut = 0;
38122 }
38123 if (ppPCMFramesOut != NULL) {
38124 *ppPCMFramesOut = NULL;
38125 }
38126
38127 if (pFilePath == NULL) {
38128 return MA_INVALID_ARGS;
38129 }
38130
38131 config = ma_decoder_config_init_copy(pConfig);
38132
38133 result = ma_decoder_init_file(pFilePath, &config, &decoder);
38134 if (result != MA_SUCCESS) {
38135 return result;
38136 }
38137
38138 return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
38139}
38140#endif
38141
38142ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
38143{
38144 ma_decoder_config config;
38145 ma_decoder decoder;
38146 ma_result result;
38147
38148 if (pFrameCountOut != NULL) {
38149 *pFrameCountOut = 0;
38150 }
38151 if (ppPCMFramesOut != NULL) {
38152 *ppPCMFramesOut = NULL;
38153 }
38154
38155 if (pData == NULL || dataSize == 0) {
38156 return MA_INVALID_ARGS;
38157 }
38158
38159 config = ma_decoder_config_init_copy(pConfig);
38160
38161 result = ma_decoder_init_memory(pData, dataSize, &config, &decoder);
38162 if (result != MA_SUCCESS) {
38163 return result;
38164 }
38165
38166 return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
38167}
38168
38169#endif /* MA_NO_DECODING */
38170
38171
38172
38173
38174/**************************************************************************************************************************************************************
38175
38176Generation
38177
38178**************************************************************************************************************************************************************/
38179ma_result ma_waveform_init(ma_waveform_type type, double amplitude, double frequency, ma_uint32 sampleRate, ma_waveform* pWaveform)
38180{
38181 if (pWaveform == NULL) {
38182 return MA_INVALID_ARGS;
38183 }
38184
38185 MA_ZERO_OBJECT(pWaveform);
38186
38187 pWaveform->type = type;
38188 pWaveform->amplitude = amplitude;
38189 pWaveform->frequency = frequency;
38190 pWaveform->deltaTime = 1.0 / sampleRate;
38191 pWaveform->time = 0;
38192
38193 return MA_SUCCESS;
38194}
38195
38196ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude)
38197{
38198 if (pWaveform == NULL) {
38199 return MA_INVALID_ARGS;
38200 }
38201
38202 pWaveform->amplitude = amplitude;
38203 return MA_SUCCESS;
38204}
38205
38206ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency)
38207{
38208 if (pWaveform == NULL) {
38209 return MA_INVALID_ARGS;
38210 }
38211
38212 pWaveform->frequency = frequency;
38213 return MA_SUCCESS;
38214}
38215
38216ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate)
38217{
38218 if (pWaveform == NULL) {
38219 return MA_INVALID_ARGS;
38220 }
38221
38222 pWaveform->deltaTime = 1.0 / sampleRate;
38223 return MA_SUCCESS;
38224}
38225
38226static float ma_waveform_sine_f32(double time, double frequency, double amplitude)
38227{
38228 return (float)(ma_sin(MA_TAU_D * time * frequency) * amplitude);
38229}
38230
38231static float ma_waveform_square_f32(double time, double frequency, double amplitude)
38232{
38233 double t = time * frequency;
38234 double f = t - (ma_uint64)t;
38235 double r;
38236
38237 if (f < 0.5) {
38238 r = amplitude;
38239 } else {
38240 r = -amplitude;
38241 }
38242
38243 return (float)r;
38244}
38245
38246static float ma_waveform_triangle_f32(double time, double frequency, double amplitude)
38247{
38248 double t = time * frequency;
38249 double f = t - (ma_uint64)t;
38250 double r;
38251
38252 r = 2 * ma_abs(2 * (f - 0.5)) - 1;
38253
38254 return (float)(r * amplitude);
38255}
38256
38257static float ma_waveform_sawtooth_f32(double time, double frequency, double amplitude)
38258{
38259 double t = time * frequency;
38260 double f = t - (ma_uint64)t;
38261 double r;
38262
38263 r = 2 * (f - 0.5);
38264
38265 return (float)(r * amplitude);
38266}
38267
38268static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
38269{
38270 ma_uint64 iFrame;
38271 ma_uint64 iChannel;
38272 ma_uint32 bpf = ma_get_bytes_per_frame(format, channels);
38273 ma_uint32 bps = ma_get_bytes_per_sample(format);
38274
38275 MA_ASSERT(pWaveform != NULL);
38276 MA_ASSERT(pFramesOut != NULL);
38277
38278 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
38279 float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->frequency, pWaveform->amplitude);
38280 pWaveform->time += pWaveform->deltaTime;
38281
38282 for (iChannel = 0; iChannel < channels; iChannel += 1) {
38283 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), format, &s, ma_format_f32, 1, ma_dither_mode_none);
38284 }
38285 }
38286}
38287
38288static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
38289{
38290 ma_uint64 iFrame;
38291 ma_uint64 iChannel;
38292 ma_uint32 bpf = ma_get_bytes_per_frame(format, channels);
38293 ma_uint32 bps = ma_get_bytes_per_sample(format);
38294
38295 MA_ASSERT(pWaveform != NULL);
38296 MA_ASSERT(pFramesOut != NULL);
38297
38298 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
38299 float s = ma_waveform_square_f32(pWaveform->time, pWaveform->frequency, pWaveform->amplitude);
38300 pWaveform->time += pWaveform->deltaTime;
38301
38302 for (iChannel = 0; iChannel < channels; iChannel += 1) {
38303 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), format, &s, ma_format_f32, 1, ma_dither_mode_none);
38304 }
38305 }
38306}
38307
38308static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
38309{
38310 ma_uint64 iFrame;
38311 ma_uint64 iChannel;
38312 ma_uint32 bpf = ma_get_bytes_per_frame(format, channels);
38313 ma_uint32 bps = ma_get_bytes_per_sample(format);
38314
38315 MA_ASSERT(pWaveform != NULL);
38316 MA_ASSERT(pFramesOut != NULL);
38317
38318 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
38319 float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->frequency, pWaveform->amplitude);
38320 pWaveform->time += pWaveform->deltaTime;
38321
38322 for (iChannel = 0; iChannel < channels; iChannel += 1) {
38323 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), format, &s, ma_format_f32, 1, ma_dither_mode_none);
38324 }
38325 }
38326}
38327
38328static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
38329{
38330 ma_uint64 iFrame;
38331 ma_uint64 iChannel;
38332 ma_uint32 bpf = ma_get_bytes_per_frame(format, channels);
38333 ma_uint32 bps = ma_get_bytes_per_sample(format);
38334
38335 MA_ASSERT(pWaveform != NULL);
38336 MA_ASSERT(pFramesOut != NULL);
38337
38338 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
38339 float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->frequency, pWaveform->amplitude);
38340 pWaveform->time += pWaveform->deltaTime;
38341
38342 for (iChannel = 0; iChannel < channels; iChannel += 1) {
38343 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), format, &s, ma_format_f32, 1, ma_dither_mode_none);
38344 }
38345 }
38346}
38347
38348ma_uint64 ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
38349{
38350 if (pWaveform == NULL) {
38351 return 0;
38352 }
38353
38354 if (pFramesOut != NULL) {
38355 switch (pWaveform->type)
38356 {
38357 case ma_waveform_type_sine:
38358 {
38359 ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount, format, channels);
38360 } break;
38361
38362 case ma_waveform_type_square:
38363 {
38364 ma_waveform_read_pcm_frames__square(pWaveform, pFramesOut, frameCount, format, channels);
38365 } break;
38366
38367 case ma_waveform_type_triangle:
38368 {
38369 ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount, format, channels);
38370 } break;
38371
38372 case ma_waveform_type_sawtooth:
38373 {
38374 ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount, format, channels);
38375 } break;
38376
38377 default: return 0;
38378 }
38379 } else {
38380 pWaveform->time += pWaveform->deltaTime * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */
38381 }
38382
38383 return frameCount;
38384}
38385
38386
38387/* End globally disabled warnings. */
38388#if defined(_MSC_VER)
38389 #pragma warning(pop)
38390#endif
38391
38392#endif /* MINIAUDIO_IMPLEMENTATION */
38393
38394/*
38395MAJOR CHANGES IN VERSION 0.9
38396============================
38397Version 0.9 includes major API changes, centered mostly around full-duplex and the rebrand to "miniaudio". Before I go into
38398detail about the major changes I would like to apologize. I know it's annoying dealing with breaking API changes, but I think
38399it's best to get these changes out of the way now while the library is still relatively young and unknown.
38400
38401There's been a lot of refactoring with this release so there's a good chance a few bugs have been introduced. I apologize in
38402advance for this. You may want to hold off on upgrading for the short term if you're worried. If mini_al v0.8.14 works for
38403you, and you don't need full-duplex support, you can avoid upgrading (though you won't be getting future bug fixes).
38404
38405
38406Rebranding to "miniaudio"
38407-------------------------
38408The decision was made to rename mini_al to miniaudio. Don't worry, it's the same project. The reason for this is simple:
38409
384101) Having the word "audio" in the title makes it immediately clear that the library is related to audio; and
384112) I don't like the look of the underscore.
38412
38413This rebrand has necessitated a change in namespace from "mal" to "ma". I know this is annoying, and I apologize, but it's
38414better to get this out of the road now rather than later. Also, since there are necessary API changes for full-duplex support
38415I think it's better to just get the namespace change over and done with at the same time as the full-duplex changes. I'm hoping
38416this will be the last of the major API changes. Fingers crossed!
38417
38418The implementation define is now "#define MINIAUDIO_IMPLEMENTATION". You can also use "#define MA_IMPLEMENTATION" if that's
38419your preference.
38420
38421
38422Full-Duplex Support
38423-------------------
38424The major feature added to version 0.9 is full-duplex. This has necessitated a few API changes.
38425
384261) The data callback has now changed. Previously there was one type of callback for playback and another for capture. I wanted
38427 to avoid a third callback just for full-duplex so the decision was made to break this API and unify the callbacks. Now,
38428 there is just one callback which is the same for all three modes (playback, capture, duplex). The new callback looks like
38429 the following:
38430
38431 void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
38432
38433 This callback allows you to move data straight out of the input buffer and into the output buffer in full-duplex mode. In
38434 playback-only mode, pInput will be null. Likewise, pOutput will be null in capture-only mode. The sample count is no longer
38435 returned from the callback since it's not necessary for miniaudio anymore.
38436
384372) The device config needed to change in order to support full-duplex. Full-duplex requires the ability to allow the client
38438 to choose a different PCM format for the playback and capture sides. The old ma_device_config object simply did not allow
38439 this and needed to change. With these changes you now specify the device ID, format, channels, channel map and share mode
38440 on a per-playback and per-capture basis (see example below). The sample rate must be the same for playback and capture.
38441
38442 Since the device config API has changed I have also decided to take the opportunity to simplify device initialization. Now,
38443 the device ID, device type and callback user data are set in the config. ma_device_init() is now simplified down to taking
38444 just the context, device config and a pointer to the device object being initialized. The rationale for this change is that
38445 it just makes more sense to me that these are set as part of the config like everything else.
38446
38447 Example device initialization:
38448
38449 ma_device_config config = ma_device_config_init(ma_device_type_duplex); // Or ma_device_type_playback or ma_device_type_capture.
38450 config.playback.pDeviceID = &myPlaybackDeviceID; // Or NULL for the default playback device.
38451 config.playback.format = ma_format_f32;
38452 config.playback.channels = 2;
38453 config.capture.pDeviceID = &myCaptureDeviceID; // Or NULL for the default capture device.
38454 config.capture.format = ma_format_s16;
38455 config.capture.channels = 1;
38456 config.sampleRate = 44100;
38457 config.dataCallback = data_callback;
38458 config.pUserData = &myUserData;
38459
38460 result = ma_device_init(&myContext, &config, &device);
38461 if (result != MA_SUCCESS) {
38462 ... handle error ...
38463 }
38464
38465 Note that the "onDataCallback" member of ma_device_config has been renamed to "dataCallback". Also, "onStopCallback" has
38466 been renamed to "stopCallback".
38467
38468This is the first pass for full-duplex and there is a known bug. You will hear crackling on the following backends when sample
38469rate conversion is required for the playback device:
38470 - Core Audio
38471 - JACK
38472 - AAudio
38473 - OpenSL
38474 - WebAudio
38475
38476In addition to the above, not all platforms have been absolutely thoroughly tested simply because I lack the hardware for such
38477thorough testing. If you experience a bug, an issue report on GitHub or an email would be greatly appreciated (and a sample
38478program that reproduces the issue if possible).
38479
38480
38481Other API Changes
38482-----------------
38483In addition to the above, the following API changes have been made:
38484
38485- The log callback is no longer passed to ma_context_config_init(). Instead you need to set it manually after initialization.
38486- The onLogCallback member of ma_context_config has been renamed to "logCallback".
38487- 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)
38488 - You can use ma_log_level_to_string() to convert the logLevel to human readable text if you want to log it.
38489- Some APIs have been renamed:
38490 - mal_decoder_read() -> ma_decoder_read_pcm_frames()
38491 - mal_decoder_seek_to_frame() -> ma_decoder_seek_to_pcm_frame()
38492 - mal_sine_wave_read() -> ma_sine_wave_read_f32()
38493 - mal_sine_wave_read_ex() -> ma_sine_wave_read_f32_ex()
38494- Some APIs have been removed:
38495 - mal_device_get_buffer_size_in_bytes()
38496 - mal_device_set_recv_callback()
38497 - mal_device_set_send_callback()
38498 - mal_src_set_input_sample_rate()
38499 - mal_src_set_output_sample_rate()
38500- Error codes have been rearranged. If you're a binding maintainer you will need to update.
38501- The ma_backend enums have been rearranged to priority order. The rationale for this is to simplify automatic backend selection
38502 and to make it easier to see the priority. If you're a binding maintainer you will need to update.
38503- ma_dsp has been renamed to ma_pcm_converter. The rationale for this change is that I'm expecting "ma_dsp" to conflict with
38504 some future planned high-level APIs.
38505- For functions that take a pointer/count combo, such as ma_decoder_read_pcm_frames(), the parameter order has changed so that
38506 the pointer comes before the count. The rationale for this is to keep it consistent with things like memcpy().
38507
38508
38509Miscellaneous Changes
38510---------------------
38511The following miscellaneous changes have also been made.
38512
38513- The AAudio backend has been added for Android 8 and above. This is Android's new "High-Performance Audio" API. (For the
38514 record, this is one of the nicest audio APIs out there, just behind the BSD audio APIs).
38515- The WebAudio backend has been added. This is based on ScriptProcessorNode. This removes the need for SDL.
38516- The SDL and OpenAL backends have been removed. These were originally implemented to add support for platforms for which miniaudio
38517 was not explicitly supported. These are no longer needed and have therefore been removed.
38518- Device initialization now fails if the requested share mode is not supported. If you ask for exclusive mode, you either get an
38519 exclusive mode device, or an error. The rationale for this change is to give the client more control over how to handle cases
38520 when the desired shared mode is unavailable.
38521- A lock-free ring buffer API has been added. There are two varients of this. "ma_rb" operates on bytes, whereas "ma_pcm_rb"
38522 operates on PCM frames.
38523- The library is now licensed as a choice of Public Domain (Unlicense) _or_ MIT-0 (No Attribution) which is the same as MIT, but
38524 removes the attribution requirement. The rationale for this is to support countries that don't recognize public domain.
38525*/
38526
38527/*
38528REVISION HISTORY
38529================
38530v0.xx.xx - 2020-xx-xx
38531 - Fix potential crash when ma_device object's are not aligned to MA_SIMD_ALIGNMENT.
38532
38533v0.9.10 - 2020-01-15
38534 - Fix compilation errors due to #if/#endif mismatches.
38535 - WASAPI: Fix a bug where automatic stream routing is being performed for devices that are initialized with an explicit device ID.
38536 - iOS: Fix a crash on device uninitialization.
38537
38538v0.9.9 - 2020-01-09
38539 - Fix compilation errors with MinGW.
38540 - Fix compilation errors when compiling on Apple platforms.
38541 - WASAPI: Add support for disabling hardware offloading.
38542 - WASAPI: Add support for disabling automatic stream routing.
38543 - Core Audio: Fix bugs in the case where the internal device uses deinterleaved buffers.
38544 - Core Audio: Add support for controlling the session category (AVAudioSessionCategory) and options (AVAudioSessionCategoryOptions).
38545 - JACK: Fix bug where incorrect ports are connected.
38546
38547v0.9.8 - 2019-10-07
38548 - WASAPI: Fix a potential deadlock when starting a full-duplex device.
38549 - WASAPI: Enable automatic resampling by default. Disable with config.wasapi.noAutoConvertSRC.
38550 - Core Audio: Fix bugs with automatic stream routing.
38551 - Add support for controlling whether or not the content of the output buffer passed in to the data callback is pre-initialized
38552 to zero. By default it will be initialized to zero, but this can be changed by setting noPreZeroedOutputBuffer in the device
38553 config. Setting noPreZeroedOutputBuffer to true will leave the contents undefined.
38554 - Add support for clipping samples after the data callback has returned. This only applies when the playback sample format is
38555 configured as ma_format_f32. If you are doing clipping yourself, you can disable this overhead by setting noClip to true in
38556 the device config.
38557 - Add support for master volume control for devices.
38558 - 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.
38559 - Use ma_device_set_master_gain_db() to set the volume in decibels where 0 is full volume and < 0 reduces the volume.
38560 - Fix warnings emitted by GCC when `__inline__` is undefined or defined as nothing.
38561
38562v0.9.7 - 2019-08-28
38563 - Add support for loopback mode (WASAPI only).
38564 - To use this, set the device type to ma_device_type_loopback, and then fill out the capture section of the device config.
38565 - If you need to capture from a specific output device, set the capture device ID to that of a playback device.
38566 - Fix a crash when an error is posted in ma_device_init().
38567 - Fix a compilation error when compiling for ARM architectures.
38568 - Fix a bug with the audio(4) backend where the device is incorrectly being opened in non-blocking mode.
38569 - Fix memory leaks in the Core Audio backend.
38570 - Minor refactoring to the WinMM, ALSA, PulseAudio, OSS, audio(4), sndio and null backends.
38571
38572v0.9.6 - 2019-08-04
38573 - Add support for loading decoders using a wchar_t string for file paths.
38574 - Don't trigger an assert when ma_device_start() is called on a device that is already started. This will now log a warning
38575 and return MA_INVALID_OPERATION. The same applies for ma_device_stop().
38576 - Try fixing an issue with PulseAudio taking a long time to start playback.
38577 - Fix a bug in ma_convert_frames() and ma_convert_frames_ex().
38578 - Fix memory leaks in the WASAPI backend.
38579 - Fix a compilation error with Visual Studio 2010.
38580
38581v0.9.5 - 2019-05-21
38582 - Add logging to ma_dlopen() and ma_dlsym().
38583 - Add ma_decoder_get_length_in_pcm_frames().
38584 - Fix a bug with capture on the OpenSL|ES backend.
38585 - Fix a bug with the ALSA backend where a device would not restart after being stopped.
38586
38587v0.9.4 - 2019-05-06
38588 - Add support for C89. With this change, miniaudio should compile clean with GCC/Clang with "-std=c89 -ansi -pedantic" and
38589 Microsoft compilers back to VC6. Other compilers should also work, but have not been tested.
38590
38591v0.9.3 - 2019-04-19
38592 - Fix compiler errors on GCC when compiling with -std=c99.
38593
38594v0.9.2 - 2019-04-08
38595 - Add support for per-context user data.
38596 - Fix a potential bug with context configs.
38597 - Fix some bugs with PulseAudio.
38598
38599v0.9.1 - 2019-03-17
38600 - Fix a bug where the output buffer is not getting zeroed out before calling the data callback. This happens when
38601 the device is running in passthrough mode (not doing any data conversion).
38602 - Fix an issue where the data callback is getting called too frequently on the WASAPI and DirectSound backends.
38603 - Fix error on the UWP build.
38604 - Fix a build error on Apple platforms.
38605
38606v0.9 - 2019-03-06
38607 - Rebranded to "miniaudio". All namespaces have been renamed from "mal" to "ma".
38608 - API CHANGE: ma_device_init() and ma_device_config_init() have changed significantly:
38609 - The device type, device ID and user data pointer have moved from ma_device_init() to the config.
38610 - All variations of ma_device_config_init_*() have been removed in favor of just ma_device_config_init().
38611 - ma_device_config_init() now takes only one parameter which is the device type. All other properties need
38612 to be set on the returned object directly.
38613 - The onDataCallback and onStopCallback members of ma_device_config have been renamed to "dataCallback"
38614 and "stopCallback".
38615 - The ID of the physical device is now split into two: one for the playback device and the other for the
38616 capture device. This is required for full-duplex. These are named "pPlaybackDeviceID" and "pCaptureDeviceID".
38617 - API CHANGE: The data callback has changed. It now uses a unified callback for all device types rather than
38618 being separate for each. It now takes two pointers - one containing input data and the other output data. This
38619 design in required for full-duplex. The return value is now void instead of the number of frames written. The
38620 new callback looks like the following:
38621 void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
38622 - API CHANGE: Remove the log callback parameter from ma_context_config_init(). With this change,
38623 ma_context_config_init() now takes no parameters and the log callback is set via the structure directly. The
38624 new policy for config initialization is that only mandatory settings are passed in to *_config_init(). The
38625 "onLog" member of ma_context_config has been renamed to "logCallback".
38626 - API CHANGE: Remove ma_device_get_buffer_size_in_bytes().
38627 - API CHANGE: Rename decoding APIs to "pcm_frames" convention.
38628 - mal_decoder_read() -> ma_decoder_read_pcm_frames()
38629 - mal_decoder_seek_to_frame() -> ma_decoder_seek_to_pcm_frame()
38630 - API CHANGE: Rename sine wave reading APIs to f32 convention.
38631 - mal_sine_wave_read() -> ma_sine_wave_read_f32()
38632 - mal_sine_wave_read_ex() -> ma_sine_wave_read_f32_ex()
38633 - API CHANGE: Remove some deprecated APIs
38634 - mal_device_set_recv_callback()
38635 - mal_device_set_send_callback()
38636 - mal_src_set_input_sample_rate()
38637 - mal_src_set_output_sample_rate()
38638 - API CHANGE: Add log level to the log callback. New signature:
38639 - void on_log(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
38640 - API CHANGE: Changes to result codes. Constants have changed and unused codes have been removed. If you're
38641 a binding mainainer you will need to update your result code constants.
38642 - API CHANGE: Change the order of the ma_backend enums to priority order. If you are a binding maintainer, you
38643 will need to update.
38644 - API CHANGE: Rename mal_dsp to ma_pcm_converter. All functions have been renamed from mal_dsp_*() to
38645 ma_pcm_converter_*(). All structures have been renamed from mal_dsp* to ma_pcm_converter*.
38646 - API CHANGE: Reorder parameters of ma_decoder_read_pcm_frames() to be consistent with the new parameter order scheme.
38647 - The resampling algorithm has been changed from sinc to linear. The rationale for this is that the sinc implementation
38648 is too inefficient right now. This will hopefully be improved at a later date.
38649 - Device initialization will no longer fall back to shared mode if exclusive mode is requested but is unusable.
38650 With this change, if you request an device in exclusive mode, but exclusive mode is not supported, it will not
38651 automatically fall back to shared mode. The client will need to reinitialize the device in shared mode if that's
38652 what they want.
38653 - Add ring buffer API. This is ma_rb and ma_pcm_rb, the difference being that ma_rb operates on bytes and
38654 ma_pcm_rb operates on PCM frames.
38655 - Add Web Audio backend. This is used when compiling with Emscripten. The SDL backend, which was previously
38656 used for web support, will be removed in a future version.
38657 - Add AAudio backend (Android Audio). This is the new priority backend for Android. Support for AAudio starts
38658 with Android 8. OpenSL|ES is used as a fallback for older versions of Android.
38659 - Remove OpenAL and SDL backends.
38660 - Fix a possible deadlock when rapidly stopping the device after it has started.
38661 - Update documentation.
38662 - Change licensing to a choice of public domain _or_ MIT-0 (No Attribution).
38663
38664v0.8.14 - 2018-12-16
38665 - Core Audio: Fix a bug where the device state is not set correctly after stopping.
38666 - Add support for custom weights to the channel router.
38667 - Update decoders to use updated APIs in dr_flac, dr_mp3 and dr_wav.
38668
38669v0.8.13 - 2018-12-04
38670 - Core Audio: Fix a bug with channel mapping.
38671 - Fix a bug with channel routing where the back/left and back/right channels have the wrong weight.
38672
38673v0.8.12 - 2018-11-27
38674 - Drop support for SDL 1.2. The Emscripten build now requires "-s USE_SDL=2".
38675 - Fix a linking error with ALSA.
38676 - Fix a bug on iOS where the device name is not set correctly.
38677
38678v0.8.11 - 2018-11-21
38679 - iOS bug fixes.
38680 - Minor tweaks to PulseAudio.
38681
38682v0.8.10 - 2018-10-21
38683 - Core Audio: Fix a hang when uninitializing a device.
38684 - Fix a bug where an incorrect value is returned from mal_device_stop().
38685
38686v0.8.9 - 2018-09-28
38687 - Fix a bug with the SDL backend where device initialization fails.
38688
38689v0.8.8 - 2018-09-14
38690 - Fix Linux build with the ALSA backend.
38691 - Minor documentation fix.
38692
38693v0.8.7 - 2018-09-12
38694 - Fix a bug with UWP detection.
38695
38696v0.8.6 - 2018-08-26
38697 - Automatically switch the internal device when the default device is unplugged. Note that this is still in the
38698 early stages and not all backends handle this the same way. As of this version, this will not detect a default
38699 device switch when changed from the operating system's audio preferences (unless the backend itself handles
38700 this automatically). This is not supported in exclusive mode.
38701 - WASAPI and Core Audio: Add support for stream routing. When the application is using a default device and the
38702 user switches the default device via the operating system's audio preferences, miniaudio will automatically switch
38703 the internal device to the new default. This is not supported in exclusive mode.
38704 - WASAPI: Add support for hardware offloading via IAudioClient2. Only supported on Windows 8 and newer.
38705 - WASAPI: Add support for low-latency shared mode via IAudioClient3. Only supported on Windows 10 and newer.
38706 - Add support for compiling the UWP build as C.
38707 - mal_device_set_recv_callback() and mal_device_set_send_callback() have been deprecated. You must now set this
38708 when the device is initialized with mal_device_init*(). These will be removed in version 0.9.0.
38709
38710v0.8.5 - 2018-08-12
38711 - Add support for specifying the size of a device's buffer in milliseconds. You can still set the buffer size in
38712 frames if that suits you. When bufferSizeInFrames is 0, bufferSizeInMilliseconds will be used. If both are non-0
38713 then bufferSizeInFrames will take priority. If both are set to 0 the default buffer size is used.
38714 - Add support for the audio(4) backend to OpenBSD.
38715 - Fix a bug with the ALSA backend that was causing problems on Raspberry Pi. This significantly improves the
38716 Raspberry Pi experience.
38717 - Fix a bug where an incorrect number of samples is returned from sinc resampling.
38718 - Add support for setting the value to be passed to internal calls to CoInitializeEx().
38719 - WASAPI and WinMM: Stop the device when it is unplugged.
38720
38721v0.8.4 - 2018-08-06
38722 - Add sndio backend for OpenBSD.
38723 - Add audio(4) backend for NetBSD.
38724 - Drop support for the OSS backend on everything except FreeBSD and DragonFly BSD.
38725 - Formats are now native-endian (were previously little-endian).
38726 - Mark some APIs as deprecated:
38727 - mal_src_set_input_sample_rate() and mal_src_set_output_sample_rate() are replaced with mal_src_set_sample_rate().
38728 - mal_dsp_set_input_sample_rate() and mal_dsp_set_output_sample_rate() are replaced with mal_dsp_set_sample_rate().
38729 - Fix a bug when capturing using the WASAPI backend.
38730 - Fix some aliasing issues with resampling, specifically when increasing the sample rate.
38731 - Fix warnings.
38732
38733v0.8.3 - 2018-07-15
38734 - Fix a crackling bug when resampling in capture mode.
38735 - Core Audio: Fix a bug where capture does not work.
38736 - ALSA: Fix a bug where the worker thread can get stuck in an infinite loop.
38737 - PulseAudio: Fix a bug where mal_context_init() succeeds when PulseAudio is unusable.
38738 - JACK: Fix a bug where mal_context_init() succeeds when JACK is unusable.
38739
38740v0.8.2 - 2018-07-07
38741 - Fix a bug on macOS with Core Audio where the internal callback is not called.
38742
38743v0.8.1 - 2018-07-06
38744 - Fix compilation errors and warnings.
38745
38746v0.8 - 2018-07-05
38747 - Changed MAL_IMPLEMENTATION to MINI_AL_IMPLEMENTATION for consistency with other libraries. The old
38748 way is still supported for now, but you should update as it may be removed in the future.
38749 - API CHANGE: Replace device enumeration APIs. mal_enumerate_devices() has been replaced with
38750 mal_context_get_devices(). An additional low-level device enumration API has been introduced called
38751 mal_context_enumerate_devices() which uses a callback to report devices.
38752 - API CHANGE: Rename mal_get_sample_size_in_bytes() to mal_get_bytes_per_sample() and add
38753 mal_get_bytes_per_frame().
38754 - API CHANGE: Replace mal_device_config.preferExclusiveMode with mal_device_config.shareMode.
38755 - This new config can be set to mal_share_mode_shared (default) or mal_share_mode_exclusive.
38756 - API CHANGE: Remove excludeNullDevice from mal_context_config.alsa.
38757 - API CHANGE: Rename MAL_MAX_SAMPLE_SIZE_IN_BYTES to MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES.
38758 - API CHANGE: Change the default channel mapping to the standard Microsoft mapping.
38759 - API CHANGE: Remove backend-specific result codes.
38760 - API CHANGE: Changes to the format conversion APIs (mal_pcm_f32_to_s16(), etc.)
38761 - Add support for Core Audio (Apple).
38762 - Add support for PulseAudio.
38763 - This is the highest priority backend on Linux (higher priority than ALSA) since it is commonly
38764 installed by default on many of the popular distros and offer's more seamless integration on
38765 platforms where PulseAudio is used. In addition, if PulseAudio is installed and running (which
38766 is extremely common), it's better to just use PulseAudio directly rather than going through the
38767 "pulse" ALSA plugin (which is what the "default" ALSA device is likely set to).
38768 - Add support for JACK.
38769 - Remove dependency on asound.h for the ALSA backend. This means the ALSA development packages are no
38770 longer required to build miniaudio.
38771 - Remove dependency on dsound.h for the DirectSound backend. This fixes build issues with some
38772 distributions of MinGW.
38773 - Remove dependency on audioclient.h for the WASAPI backend. This fixes build issues with some
38774 distributions of MinGW.
38775 - Add support for dithering to format conversion.
38776 - Add support for configuring the priority of the worker thread.
38777 - Add a sine wave generator.
38778 - Improve efficiency of sample rate conversion.
38779 - Introduce the notion of standard channel maps. Use mal_get_standard_channel_map().
38780 - Introduce the notion of default device configurations. A default config uses the same configuration
38781 as the backend's internal device, and as such results in a pass-through data transmission pipeline.
38782 - Add support for passing in NULL for the device config in mal_device_init(), which uses a default
38783 config. This requires manually calling mal_device_set_send/recv_callback().
38784 - Add support for decoding from raw PCM data (mal_decoder_init_raw(), etc.)
38785 - Make mal_device_init_ex() more robust.
38786 - Make some APIs more const-correct.
38787 - Fix errors with SDL detection on Apple platforms.
38788 - Fix errors with OpenAL detection.
38789 - Fix some memory leaks.
38790 - Fix a bug with opening decoders from memory.
38791 - Early work on SSE2, AVX2 and NEON optimizations.
38792 - Miscellaneous bug fixes.
38793 - Documentation updates.
38794
38795v0.7 - 2018-02-25
38796 - API CHANGE: Change mal_src_read_frames() and mal_dsp_read_frames() to use 64-bit sample counts.
38797 - Add decoder APIs for loading WAV, FLAC, Vorbis and MP3 files.
38798 - Allow opening of devices without a context.
38799 - In this case the context is created and managed internally by the device.
38800 - Change the default channel mapping to the same as that used by FLAC.
38801 - Fix build errors with macOS.
38802
38803v0.6c - 2018-02-12
38804 - Fix build errors with BSD/OSS.
38805
38806v0.6b - 2018-02-03
38807 - Fix some warnings when compiling with Visual C++.
38808
38809v0.6a - 2018-01-26
38810 - Fix errors with channel mixing when increasing the channel count.
38811 - Improvements to the build system for the OpenAL backend.
38812 - Documentation fixes.
38813
38814v0.6 - 2017-12-08
38815 - API CHANGE: Expose and improve mutex APIs. If you were using the mutex APIs before this version you'll
38816 need to update.
38817 - API CHANGE: SRC and DSP callbacks now take a pointer to a mal_src and mal_dsp object respectively.
38818 - API CHANGE: Improvements to event and thread APIs. These changes make these APIs more consistent.
38819 - Add support for SDL and Emscripten.
38820 - Simplify the build system further for when development packages for various backends are not installed.
38821 With this change, when the compiler supports __has_include, backends without the relevant development
38822 packages installed will be ignored. This fixes the build for old versions of MinGW.
38823 - Fixes to the Android build.
38824 - Add mal_convert_frames(). This is a high-level helper API for performing a one-time, bulk conversion of
38825 audio data to a different format.
38826 - Improvements to f32 -> u8/s16/s24/s32 conversion routines.
38827 - Fix a bug where the wrong value is returned from mal_device_start() for the OpenSL backend.
38828 - Fixes and improvements for Raspberry Pi.
38829 - Warning fixes.
38830
38831v0.5 - 2017-11-11
38832 - API CHANGE: The mal_context_init() function now takes a pointer to a mal_context_config object for
38833 configuring the context. The works in the same kind of way as the device config. The rationale for this
38834 change is to give applications better control over context-level properties, add support for backend-
38835 specific configurations, and support extensibility without breaking the API.
38836 - API CHANGE: The alsa.preferPlugHW device config variable has been removed since it's not really useful for
38837 anything anymore.
38838 - ALSA: By default, device enumeration will now only enumerate over unique card/device pairs. Applications
38839 can enable verbose device enumeration by setting the alsa.useVerboseDeviceEnumeration context config
38840 variable.
38841 - ALSA: When opening a device in shared mode (the default), the dmix/dsnoop plugin will be prioritized. If
38842 this fails it will fall back to the hw plugin. With this change the preferExclusiveMode config is now
38843 honored. Note that this does not happen when alsa.useVerboseDeviceEnumeration is set to true (see above)
38844 which is by design.
38845 - ALSA: Add support for excluding the "null" device using the alsa.excludeNullDevice context config variable.
38846 - ALSA: Fix a bug with channel mapping which causes an assertion to fail.
38847 - Fix errors with enumeration when pInfo is set to NULL.
38848 - OSS: Fix a bug when starting a device when the client sends 0 samples for the initial buffer fill.
38849
38850v0.4 - 2017-11-05
38851 - API CHANGE: The log callback is now per-context rather than per-device and as is thus now passed to
38852 mal_context_init(). The rationale for this change is that it allows applications to capture diagnostic
38853 messages at the context level. Previously this was only available at the device level.
38854 - API CHANGE: The device config passed to mal_device_init() is now const.
38855 - Added support for OSS which enables support on BSD platforms.
38856 - Added support for WinMM (waveOut/waveIn).
38857 - Added support for UWP (Universal Windows Platform) applications. Currently C++ only.
38858 - Added support for exclusive mode for selected backends. Currently supported on WASAPI.
38859 - POSIX builds no longer require explicit linking to libpthread (-lpthread).
38860 - ALSA: Explicit linking to libasound (-lasound) is no longer required.
38861 - ALSA: Latency improvements.
38862 - ALSA: Use MMAP mode where available. This can be disabled with the alsa.noMMap config.
38863 - ALSA: Use "hw" devices instead of "plughw" devices by default. This can be disabled with the
38864 alsa.preferPlugHW config.
38865 - WASAPI is now the highest priority backend on Windows platforms.
38866 - Fixed an error with sample rate conversion which was causing crackling when capturing.
38867 - Improved error handling.
38868 - Improved compiler support.
38869 - Miscellaneous bug fixes.
38870
38871v0.3 - 2017-06-19
38872 - API CHANGE: Introduced the notion of a context. The context is the highest level object and is required for
38873 enumerating and creating devices. Now, applications must first create a context, and then use that to
38874 enumerate and create devices. The reason for this change is to ensure device enumeration and creation is
38875 tied to the same backend. In addition, some backends are better suited to this design.
38876 - API CHANGE: Removed the rewinding APIs because they're too inconsistent across the different backends, hard
38877 to test and maintain, and just generally unreliable.
38878 - Added helper APIs for initializing mal_device_config objects.
38879 - Null Backend: Fixed a crash when recording.
38880 - Fixed build for UWP.
38881 - Added support for f32 formats to the OpenSL|ES backend.
38882 - Added initial implementation of the WASAPI backend.
38883 - Added initial implementation of the OpenAL backend.
38884 - Added support for low quality linear sample rate conversion.
38885 - Added early support for basic channel mapping.
38886
38887v0.2 - 2016-10-28
38888 - API CHANGE: Add user data pointer as the last parameter to mal_device_init(). The rationale for this
38889 change is to ensure the logging callback has access to the user data during initialization.
38890 - API CHANGE: Have device configuration properties be passed to mal_device_init() via a structure. Rationale:
38891 1) The number of parameters is just getting too much.
38892 2) It makes it a bit easier to add new configuration properties in the future. In particular, there's a
38893 chance there will be support added for backend-specific properties.
38894 - Dropped support for f64, A-law and Mu-law formats since they just aren't common enough to justify the
38895 added maintenance cost.
38896 - DirectSound: Increased the default buffer size for capture devices.
38897 - Added initial implementation of the OpenSL|ES backend.
38898
38899v0.1 - 2016-10-21
38900 - Initial versioned release.
38901*/
38902
38903
38904/*
38905This software is available as a choice of the following licenses. Choose
38906whichever you prefer.
38907
38908===============================================================================
38909ALTERNATIVE 1 - Public Domain (www.unlicense.org)
38910===============================================================================
38911This is free and unencumbered software released into the public domain.
38912
38913Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
38914software, either in source code form or as a compiled binary, for any purpose,
38915commercial or non-commercial, and by any means.
38916
38917In jurisdictions that recognize copyright laws, the author or authors of this
38918software dedicate any and all copyright interest in the software to the public
38919domain. We make this dedication for the benefit of the public at large and to
38920the detriment of our heirs and successors. We intend this dedication to be an
38921overt act of relinquishment in perpetuity of all present and future rights to
38922this software under copyright law.
38923
38924THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38925IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38926FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
38927AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
38928ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
38929WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38930
38931For more information, please refer to <http://unlicense.org/>
38932
38933===============================================================================
38934ALTERNATIVE 2 - MIT No Attribution
38935===============================================================================
38936Copyright 2020 David Reid
38937
38938Permission is hereby granted, free of charge, to any person obtaining a copy of
38939this software and associated documentation files (the "Software"), to deal in
38940the Software without restriction, including without limitation the rights to
38941use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
38942of the Software, and to permit persons to whom the Software is furnished to do
38943so.
38944
38945THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38946IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38947FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
38948AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38949LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38950OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38951SOFTWARE.
38952*/
38953